IP Address Not showing up in Serilog - asp.net-core

I have setup Serilog with .net 6 asp.net core project but the IP Address is not showing up in the logs using the json formatter.
var logger = new LoggerConfiguration()
.Enrich.WithClientIp()
.Enrich.WithClientAgent()
.Enrich.FromLogContext()
.ReadFrom.Configuration(builder.Configuration)
.CreateLogger();
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(logger);
And I have setup my appsettings like this. But I'm not sure why the IP Address is not showing up in the logs for any request (get, put, etc.)
"Serilog": {
"Using": [ "Serilog.Sinks.File, Serilog.Enrichers.ClientInfo" ],
"MinimumLevel": {
"Default": "Information"
},
"Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId", "WithClientIP", "WithClientAgent" ],
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "===> {Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "C://Temp//appLog_.json",
"formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog",
"rollOnFileSizeLimit": true,
"fileSizeLimitBytes": 4194304,
"retainedFileCountLimit": 10,
"rollingInterval": "Day"
}
}
]
},

In Program.cs, I change the code into below code:
builder.Host.UseSerilog((ctx, lc) => lc
.WriteTo.Console()
.ReadFrom.Configuration(ctx.Configuration));
...
app.UseSerilogRequestLogging(options =>
{
// Customize the message template
options.MessageTemplate = "{RemoteIpAddress} {RequestScheme} {RequestHost} {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms";
// Emit debug-level events instead of the defaults
options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;
// Attach additional properties to the request completion event
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
diagnosticContext.Set("RemoteIpAddress", httpContext.Connection.RemoteIpAddress);
};
});
Read net6.0 Serilog example and Adding IP logging to know more.

Related

how can I solve error 'The handle is invalid' in serilog

I have an Asp.net core web api using .net core 6.
I use Serilog to store the app log in a text file.
sometimes I get this error:
Caught exception while emitting to sink Serilog.Sinks.SystemConsole.ConsoleSink: System.IO.IOException: The handle is invalid.
at System.ConsolePal.WindowsConsoleStream.Write(ReadOnlySpan`1 buffer)
at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
at System.IO.StreamWriter.Write(String value)
at System.IO.TextWriter.SyncTextWriter.Write(String value)
at Serilog.Sinks.SystemConsole.ConsoleSink.Emit(LogEvent logEvent)
at Serilog.Core.Sinks.SafeAggregateSink.Emit(LogEvent logEvent)
could you please help me how can I solve this issue?
this is my appsetting:
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "AppLogs/bckLog-.log",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Warning"
}
},
{
"Name": "Console"
}
],
"Enrich": [
"FromLogContext"
]
}
and this is my program.cs
Serilog.Debugging.SelfLog.Enable(message => { Log.Logger?.Error(message); Console.WriteLine(message); });
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(builder.Configuration).CreateLogger();
builder.Host.UseSerilog();

How to make Serilog pick up both the expression template and a console (color) theme from appsettings.json

I'm trying to configure Serilog for my ASP.NET app using appsettings.json and am following the instructions from the GitHub Serilog.Settings.Configuration project readme. I cannot get the Console output format expression working from appsettings.json when I also set a theme. When both are configured directly in code it works just fine.
This is the Serilog section of appsettings.json. Note the full template is more complex, it uses Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1) amonst others, but this is how I am testing for now:
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"formatter": {
"type": "Serilog.Templates.ExpressionTemplate, Serilog.Expressions",
"template": "[{#t:HH:mm:ss:fff} {#l:u3}] {SourceContext,48} : {#m}\n{#x}"
},
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console"
}
}
],
"Enrich": [ "FromLogContext" ]
}
}
And this is the resulting output (with theme colors):
[17:13:38 INF] Configure
[17:13:38 INF] Now listening on: https://localhost:5001
[17:13:38 INF] Now listening on: http://localhost:5000
[17:13:38 INF] Application started. Press Ctrl+C to shut down.
Which appears to be the default format (note the msec field in the timestamp and the Source Context field are missing).
When I omit the theme setting from the json, the output looks like this, i.e. with the correct expression template applied (e.g. source context) but without any color:
[17:13:37:498 INF] App.Web.Server.Program : Server started
[17:13:37:797 INF] App.Web.Server.Startup : ConfigureServices
This is the same format I get when I configure the logger in code, but then I get the theme color too!
I also tried to use this format from the project's samples:
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {SourceContext} [{Level}] {Message}{NewLine}{Exception}",
"theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Grayscale, Serilog.Sinks.Console"
}
}
]
This is without the format expression, just the format (and a slight variation on the format itself) and, as expected, results in:
[14:35:11 Microsoft.Hosting.Lifetime [Information] Now listening on: "https://localhost:5001"
[14:35:11 Microsoft.Hosting.Lifetime [Information] Now listening on: "http://localhost:5000"
I tried umpteen variations and combinations without any success. So it seems that using appsettings.json I can either get the expression template working, or the theme working, but not both at the same time.
Again: when configured directly in code it "just works" ...
Suggestion are welcome.
I tried as below:
Packages:
in appsettings.json:
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "Time:{Timestamp: HH:mm:ss.fff} Level:{Level} DetailedInfo:{Message}{NewLine}{Exception}"
}
}
],
"Enrich": ["FromLogContext"]
}
In program.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog((context, logger) =>
{
logger.ReadFrom.Configuration(context.Configuration);
});
Result:
and with
"theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Grayscale, Serilog.Sinks.Console"

Ocelot API Gateway Custom Aggregator Issue in ASP.NET Core 3.1

I'm implementing custom Aggregator using Ocelot in ASP.NET it's throwing error in Startup.cs Ocelot middleware. However, both Microservices are working fine and fetching the data independently.
When I'm calling them from my API Gateway it's throwing below error.
Startup.cs
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddOcelot()
.AddSingletonDefinedAggregator<MyAggregator>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public async void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
await app.UseOcelot();
}
}
Here is my ocelot.json file for routes of different Microservices.
ocelot.json
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/user/getUser",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "44378"
}
],
"UpstreamPathTemplate": "/getUser",
"Key": "User"
},
{
"DownstreamPathTemplate": "/product/getProduct",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "44357"
}
],
"UpstreamPathTemplate": "/getProduct",
"Key": "Product"
}
],
"Aggregates": [
{
"ReRouteKeys": [
"User",
"Product"
],
"UpstreamPathTemplate": "/UserAndProduct"
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000/"
}
}
My custom aggregator class
MyAggregator.cs
public class MyAggregator : IDefinedAggregator
{
public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
{
var one = await responses[0].Items.DownstreamResponse().Content.ReadAsStringAsync();
var two = await responses[1].Items.DownstreamResponse().Content.ReadAsStringAsync();
var contentBuilder = new StringBuilder();
contentBuilder.Append(one);
contentBuilder.Append(two);
var stringContent = new StringContent(contentBuilder.ToString())
{
Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
};
return new DownstreamResponse(stringContent, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "OK");
}
}
You forgot to mention your custom aggregator in your ocelot.json file. Ocelot needs to know your custom aggregator whenever you hit /UserAndProduct.
"Aggregates": [
{
"ReRouteKeys": [
"User",
"Product"
],
"UpstreamPathTemplate": "/UserAndProduct"
}
]
And there is a breaking change in ocelot's latest version. Use the key
Routes instead of ReRoutes. You can use the following json file.
{
"Routes": [
{
"DownstreamPathTemplate": "/user/getUser",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "44378"
}
],
"UpstreamPathTemplate": "/getUser",
"Key": "User"
},
{
"DownstreamPathTemplate": "/product/getProduct",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "44357"
}
],
"UpstreamPathTemplate": "/getProduct",
"Key": "Product"
}
],
"Aggregates": [
{
"RouteKeys": [
"User",
"Product"
],
"UpstreamPathTemplate": "/UserAndProduct",
"Aggregator": "MyAggregator"
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000/"
}
}

Exception in Serilog Initialization

I'm attempting to try Serilog out in an asp.net core web api (3.1) but seem to be having trouble with the following:
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.log.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
//Configure logger
var loggerConfiguration = new LoggerConfiguration().ReadFrom.Configuration(configuration);
Log.Logger = loggerConfiguration.CreateLogger();
It is throwing the exception:
Exception has occurred: CLR/System.InvalidOperationException
An unhandled exception of type 'System.InvalidOperationException' occurred in Microsoft.Extensions.Configuration.Binder.dll: 'Cannot create instance of type 'System.String' because it is missing a public parameterless constructor.'
at Microsoft.Extensions.Configuration.ConfigurationBinder.CreateInstance(Type type)
at Microsoft.Extensions.Configuration.ConfigurationBinder.BindInstance(Type type, Object instance, IConfiguration config, BinderOptions options)
at Microsoft.Extensions.Configuration.ConfigurationBinder.Get(IConfiguration configuration, Type type, Action`1 configureOptions)
at Microsoft.Extensions.Configuration.ConfigurationBinder.Get(IConfiguration configuration, Type type)
at Serilog.Settings.Configuration.ObjectArgumentValue.ConvertTo(Type toType, ResolutionContext resolutionContext)
at Serilog.Settings.Configuration.ConfigurationReader.<>c__DisplayClass18_2.<CallConfigurationMethods>b__3(<>f__AnonymousType9`2 <>h__TransparentIdentifier0)
at System.Linq.Utilities.<>c__DisplayClass2_0`3.<CombineSelectors>b__0(TSource x)
at System.Linq.Enumerable.SelectListPartitionIterator`2.ToList()
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Serilog.Settings.Configuration.ConfigurationReader.CallConfigurationMethods(ILookup`2 methods, IList`1 configurationMethods, Object receiver)
at Serilog.Settings.Configuration.ConfigurationReader.ApplySinks(LoggerConfiguration loggerConfiguration)
at Serilog.Settings.Configuration.ConfigurationReader.Configure(LoggerConfiguration loggerConfiguration)
at Serilog.Configuration.LoggerSettingsConfiguration.Settings(ILoggerSettings settings)
at Serilog.ConfigurationLoggerConfigurationExtensions.Configuration(LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, String sectionName, DependencyContext dependencyContext)
at Serilog.ConfigurationLoggerConfigurationExtensions.Configuration(LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, DependencyContext dependencyContext)
My appsettings.log.json looks like this:
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console" ],
"MinimumLevel": "Debug",
"WriteTo": [
{ "Name": "Console" },
{
"Name": "File",
"Args": {
"path": "%ProgramData%\\Logs\\BoxIt\\BoxIt-{Date}.txt",
"rollOnFileSizeLimit": "true",
"rollingInterval": "Day",
"fileSizeLimitBytes": 10000000,
"outputTemplate:": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Destructure": [
{
"Name": "ToMaximumDepth",
"Args": { "maximumDestructuringDepth": 4 }
},
{
"Name": "ToMaximumStringLength",
"Args": { "maximumStringLength": 100 }
},
{
"Name": "ToMaximumCollectionCount",
"Args": { "maximumCollectionCount": 10 }
}
],
"Properties": {
"Application": "Box-It"
}
}
}
Most of the articles I've read initialize this in a very similar fashion so I'm not quite sure if there is something wrong with the config file or how I'm reading the configuration.
"outputTemplate:": was the issue here. The colon inside the double quotes was a mistake and was leading to the error above.

ConfigureKestrel() conflict with appsettings

I have Kestrel section in appsettings.json and I also call ConfigureKestrel() in Program.cs. When I run the app, there is an error:
11:10:36 [Warning] () Overriding address(es) '"https://localhost:5003"'. Binding to endpoints defined in "UseKestrel()" instead.
11:10:36 [Fatal] () Unable to start Kestrel.
System.IO.IOException: Failed to bind to address https://[::]:5003: address already in use. ---> Microsoft.AspNetCore.Connections.AddressInUseException: Address already in use ---> System.Net.Sockets.SocketException: Address already in use
Here is my appsettings.json:
"Kestrel": {
"Endpoints": {
"HttpsInlineCertFile": {
"Url": "https://localhost:5003",
"Certificate": {
"Path": "/tmp/localhost.pfx",
"Password": "password"
}
},
"HttpsDefaultCert": {
"Url": "https://localhost:5003"
},
"Https": {
"Url": "https://*:5003",
"Certificate": {
"Path": "/tmp/localhost.pfx",
"Password": "password"
}
}
},
"Certificates": {
"Default": {
"Path": "/tmp/localhost.pfx",
"Password": "password"
}
}
},
Here is my ConfigureKestrel():
.ConfigureKestrel((context, options) =>
{
options.Listen(IPAddress.Any, 5003, listenOptions =>
{
listenOptions.UseHttps(o => o.SslProtocols = SslProtocols.Tls12);
listenOptions.UseConnectionLogging();
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
});
OR:
.ConfigureKestrel((context, options) =>
{
options.Configure(context.Configuration.GetSection("Kestrel"))
.Endpoint("Https", listenOptions =>
{
listenOptions.HttpsOptions.SslProtocols = SslProtocols.Tls12;
listenOptions.ListenOptions.UseConnectionLogging();
listenOptions.ListenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
});
So, which is which? I am confused. Can I do everything in appsettings.json?
Update: I have figured out how to configure other options in appsettings.json except 2: listenOptions.HttpsOptions.SslProtocols and UseConnectionLogging