从ILogger访问当前的HttpContext

Fla*_*ien 8 dependency-injection asp.net-core asp.net-core-1.0

在ASP.NET 1.0的核心,我的一个自定义的实现ILoggerProviderILogger接口.我希望能够从该Log方法访问HttpContext .

看来我需要注入一个IHttpContextAccessor自定义ILogger,但无法找到如何做到这一点.该ILoggerProvider对象在启动时创建,该CreateLogger方法不允许依赖注入.

是否有一种简单的方法来使用依赖注入ILogger

ade*_*lin 8

这是一个例子

Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    }

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
    {
        loggerFactory.AddCustomLogger(serviceProvider.GetService<IHttpContextAccessor>());
        // 
    }
Run Code Online (Sandbox Code Playgroud)

自定义记录器:

public class CustomLogProvider : ILoggerProvider
{

    private readonly Func<string, LogLevel, bool> _filter;
    private readonly IHttpContextAccessor _accessor;

    public CustomLogProvider(Func<string, LogLevel, bool> filter, IHttpContextAccessor accessor)
    {
        _filter = filter;
        _accessor = accessor;
    }

    public ILogger CreateLogger(string categoryName)
    {
        return new CustomLogger(categoryName, _filter, _accessor);
    }

    public void Dispose()
    {
    }
}

public class CustomLogger : ILogger
{
    private string _categoryName;
    private Func<string, LogLevel, bool> _filter;
    private readonly IHttpContextAccessor _accessor;

    public CustomLogger(string categoryName, Func<string, LogLevel, bool> filter, IHttpContextAccessor accessor)
    {
        _categoryName = categoryName;
        _filter = filter;
        _accessor = accessor;
    }

    public IDisposable BeginScope<TState>(TState state)
    {
        return null;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return (_filter == null || _filter(_categoryName, logLevel));
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        if (!IsEnabled(logLevel))
        {
            return;
        }

        if (formatter == null)
        {
            throw new ArgumentNullException(nameof(formatter));
        }

        var message = formatter(state, exception);

        if (string.IsNullOrEmpty(message))
        {
            return;
        }

        message = $"{ logLevel }: {message}";

        if (exception != null)
        {
            message += Environment.NewLine + Environment.NewLine + exception.ToString();
        }
        if(_accessor.HttpContext != null) // you should check HttpContext 
        {
            message += Environment.NewLine + Environment.NewLine + _accessor.HttpContext.Request.Path;
        }
        // your implementation

    }

}
public static class CustomLoggerExtensions
{
    public static ILoggerFactory AddCustomLogger(this ILoggerFactory factory, IHttpContextAccessor accessor,
                                          Func<string, LogLevel, bool> filter = null)
    {
        factory.AddProvider(new CustomLogProvider(filter, accessor));
        return factory;
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然以上方式有效,但我更愿意实现自定义IRequestLogger而不是注入HttpContextAccessor.实现如下(未经测试):

public interface IRequestLogger<T>
{
    void Log(LogLevel logLevel, EventId eventId, string message); // you can change this
}

public class RequestLogger<T> : IRequestLogger<T>
{
    private readonly IHttpContextAccessor _accessor;
    private readonly ILogger _logger;
    public RequestLogger(ILogger<T> logger, IHttpContextAccessor accessor)
    {
        _accessor = accessor;
        _logger = logger;
    }

    public void Log(LogLevel logLevel, EventId eventId, string message)
    {
        Func<object, Exception, string> _messageFormatter = (object state, Exception error) =>
        {
            return state.ToString();
        };
        _logger.Log(LogLevel.Critical, 0, new FormattedLogValues(message), null, _messageFormatter);
    }
}
Run Code Online (Sandbox Code Playgroud)

简单用法:

public class LogType
{

}
public void ConfigureServices(IServiceCollection services)
{
     services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
     services.AddSingleton(typeof(IRequestLogger<>), typeof(RequestLogger<>));
}

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
     loggerFactory.AddConsole(true);
     app.Run(async (context) =>
     {
         var requestLogger = context.RequestServices.GetService<IRequestLogger<LogType>>();
         requestLogger.Log(LogLevel.Critical, 11, "<message>");
         //
     });
}
Run Code Online (Sandbox Code Playgroud)