I have a custom ILogger / ILoggerProvider implementation that is basically a fancy JsonConsole logger to format things as required for our console log forwarding.
Everything works fine, including using ILogger.BeginScope to pass custom data to the log method(s).
Next, I am trying to write some middleware that looks at the HttpContext and automagically adds certain fields to the log scope.
What I have so far - I've trimmed out everthing not related to the log scope:
public class MyLogger : ILogger
{
// ...
private readonly string _categoryName;
private IExternalScopeProvider _scopeProvider { get; set; }
public MyLogger(string categoryName, IExternalScopeProvider scopeProvider)
{
_categoryName = categoryName;
_scopeProvider = scopeProvider;
}
// ...
}
public class MyLoggerProvider : ILoggerProvider
{
// ...
public ILogger CreateLogger(string categoryName)
{
return _loggers.GetOrAdd(categoryName, new MyLogger(
categoryName,
new LoggerExternalScopeProvider()
));
}
// ...
}
public static ILoggingBuilder AddMyLogger(this ILoggingBuilder builder)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, MyLoggerProvider>());
return builder;
}
// Program.cs
services.AddLogging(logBuilder =>
{
logBuilder.ClearProviders();
logBuilder.AddMyLogger();
});
// !!! Everything below here is conjecture - I don't have it working yet.
public class MyAutoLogScopeMiddleware
{
private readonly RequestDelegate _next;
public MyAutoLogScopeMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext, IExternalScopeProvider scopeProvider)
{
var logInfo = "Hi there!";
using (scopeProvider.Push(logInfo))
{
await _next(httpContext);
}
}
}
public static class MyAutoLogScopeMiddlewareExtensions
{
public static IApplicationBuilder UseMyAutoLogScope(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyAutoLogScopeMiddleware>();
}
}
// Program.cs
app.UseMyAutoLogScope();
My question is how do I get the IExternalScopeProvider into the provider via dependency injection instead of newing one up?
Should I be registering it as .AddScoped<IExternalScopeProvider,LoggerExternalScopeProvider>()
, and then change the provider to .AddScoped
as well?
I feel like I should be using ISupportExternalScope.SetScopeProvider(IExternalScopeProvider scopeProvider)
on MyLoggerProvider, but I can't find any examples of how to use that with dependency injection.
If anyone has a working example, I'd really appreciate a link to a blog post or article.
Thanks.
Your logger provider should implement ISupportExternalScope
and pass along any scope provider to its loggers.
public class MyLoggerProvider : ILoggerProvider, ISupportExternalScope
{
private IExternalScopeProvider _externalScopeProvider;
void ISupportExternalScope.SetScopeProvider(IExternalScopeProvider externalScopeProvider) =>
_externalScopeProvider = externalScopeProvider
public ILogger CreateLogger(string categoryName)
{
return _loggers.GetOrAdd(categoryName, new MyLogger(
categoryName,
_externalScopeProvider
));
}
}
The logger factory system will call ISupportExternalScope.SetScopeProvider
. You don't need to do it, and you don't need to provide an ISupportExternalScope
to the DI, either.
Your application code should not call IExternalScopeProvider.Push
to create scopes; instead, use ILogger.BeginScope
:
public class MyAutoLogScopeMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<MyAutoLogScopeMiddleware> _logger;
public MyAutoLogScopeMiddleware(RequestDelegate next, ILogger<MyAutoLogScopeMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext httpContext)
{
var logInfo = "Hi there!";
using (_logger.BeginScope(logInfo))
{
await _next(httpContext);
}
}
}