从ILogger访问当前的HttpContext

时间:2016-07-14 01:28:29

标签: dependency-injection asp.net-core asp.net-core-1.0

在ASP.NET Core 1.0中,我有ILoggerProviderILogger接口的自定义实现。我希望能够从Log方法访问HttpContext。

我似乎需要在自定义IHttpContextAccessor中注入ILogger,但无法找到如何做到这一点。 ILoggerProvider对象是在启动时创建的,CreateLogger方法不允许依赖注入。

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

2 个答案:

答案 0 :(得分:6)

这是一个例子

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>());
        // 
    }

自定义记录器:

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;
    }
}

虽然以上方式有效,但我更愿意实施自定义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);
    }
}

简单用法:

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>");
         //
     });
}

答案 1 :(得分:2)

我还没有测试过这段代码,但我相信这种方法会如下所示。

Startup.cs类中,通过将以下行添加到ConfigureServices方法来注册HttpContextAccessor:

services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

然后,将IHttpContextAccessor httpContextAccessor的其他参数添加到Configure方法(仍在Startup.cs内),例如:

public void Configure(
                      IApplicationBuilder app, 
                      IHostingEnvironment env, 
                      ILoggerFactory loggerFactory, 
                      IHttpContextAccessor httpContextAccessor)

在此Configure方法中,您可以立即添加在记录器工厂上调用扩展方法来创建自定义日志提供程序,如:

loggerFactory.AddMyCustomProvider(httpContextAccessor);

扩展方法(您需要创建)将是:

public static class MyCustomLoggerExtensions
  {
    public static ILoggerFactory AddMyCustomProvider(
        this ILoggerFactory factory, 
        IHttpContextAccessor httpContextAccessor)
    {
      factory.AddProvider(new MyCustomLoggerProvider(httpContextAccessor));
      return factory;
    }
  }