Reputation: 37172
I want to get certain key bits of information (service-name, service-version, host-name, etc) logged on every log message in an ASP.Net Core service.
I have the following code:
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (host.Services.GetRequiredService<ILogger<Program>>().BeginScope("ServiceName: {0}", "bob-service"))
{
host.Run();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(builder =>
{
builder.AddConsole(o => o.IncludeScopes = true);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
From this I get the following logging:
info: Microsoft.Hosting.Lifetime[0]
=> ServiceName: bob-service
Now listening on: http://localhost:5002
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[102]
=> ConnectionId:0HLSO8706NGBN => RequestPath:/ RequestId:0HLSO8706NGBN:00000001, SpanId:|aa5794ba-408ad22a63064421., TraceId:aa5794ba-408ad22a63064421, ParentId: => Middleware_Scope => /Index
Executed handler method OnGet, returned result .
Note how the ServiceName
only appears on the host-level logging. The request-level logging does not include it.
Is there a mechanism for setting scope on every log-message that comes out of ASP.Net Core.
I've tried adding a piece of middleware to do this, and it mostly works, but the logging doesn't appear on the "Microsoft.AspNetCore.Hosting.Diagnostics"
log messages (although it appears on everything else as far as I can tell).
app
.Use(async (context, next) =>
{
// Middleware to add scoped values to log messages.
var logger = context.RequestServices.GetRequiredService<ILogger<Startup>>();
using (logger.BeginScope("ServiceName: {0}", "bob-service")))
{
await next();
}
});
Even when using the middleware described above, log messages from the Microsoft.AspNetCore.Hosting.Diagnostics
logger do not contain the scoped fields. For example, note how in the second log message, the Middleware_Scope
scoped message does not appear.
info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[6]
=> ConnectionId:0HLSOTDVELPJA => RequestPath:/css/site.css RequestId:0HLSOTDVELPJA:00000001, SpanId:|2fbd556d-44cc7c919266ccaf., TraceId:2fbd556d-44cc7c919266ccaf, ParentId: => Middleware_Scope
The file /css/site.css was not modified
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
=> ConnectionId:0HLSOTDVELPJD => RequestPath:/js/site.js RequestId:0HLSOTDVELPJD:00000001, SpanId:|2fbd5570-44cc7c919266ccaf., TraceId:2fbd5570-44cc7c919266ccaf, ParentId:
Request finished in 29.8413ms 304 application/javascript
Upvotes: 13
Views: 6478
Reputation: 191
I don't know if it is right, but I came to set scopes globally using ILoggerProvider
instead of ILogger<T>
, and setting my own scope provider for it, something like this:
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
// ... other logging configurations, like ClearProviders() and AddJsonConsole(o => o.IncludeScopes = true);
var provider = host.Services.GetService<ILoggerProvider>()
if (provider is ISupportExternalScope scopedProvider)
{
var scopes = new LoggerExternalScopeProvider();
scopes.Push("ServiceName: bob-service");
scopedProvider.SetScopeProvider(scopes);
}
host.Run();
}
You may have to use GetServices<ILoggerProvider>
and take care of a list if you have more than one provider.
Upvotes: 3