如何在SeriLog Sink中获取当前的HttpContext?

Gav*_*and 7 c# httpcontext serilog asp.net-core-2.0

我正在ILogEventSink使用构建简单接收器示例创建我自己的SeriLog 接收器,目的是记录用户声明中的一些信息.要在Core中访问HttpContext,我通常会注入一个实例,IHttpContextAccessor但示例显示在扩展方法中创建接收器的实例,例如

public class MySink : ILogEventSink
{
    private readonly IFormatProvider _formatProvider;

    public MySink(IFormatProvider formatProvider)
    {
        _formatProvider = formatProvider;
    }

    public void Emit(LogEvent logEvent)
    {
        // How to get HttpContext here?
    }
}

public static class MySinkExtensions
{
    public static LoggerConfiguration MySink(
              this LoggerSinkConfiguration loggerConfiguration,
              IFormatProvider formatProvider = null)
    {
        return loggerConfiguration.Sink(new MySink(formatProvider));
    }
}
Run Code Online (Sandbox Code Playgroud)

......然后使用水槽......

var log = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.MySink()
    .CreateLogger();
Run Code Online (Sandbox Code Playgroud)

如何在接收器的Emit方法中访问当前的HttpContext?或者是否可以使用DI框架创建的接收器?!

我有一个运行Asp.Net Core 2框架的MVC站点,使用Serilog.AspNetCore v2.1.0对抗.Net 4.6.2运行时.

更新 - 解决方法

在来自@tsimbalar的指针之后,我创建了类似于下面代码的中间件.在我的StartUp.Configure方法中,我在应用程序身份验证步骤发生app.UseMiddleware<ClaimsMiddleware>(); 使用它添加它(否则将不会加载声明).

public class ClaimsMiddleware
{
    private static readonly ILogger Log = Serilog.Log.ForContext<ClaimsMiddleware>();
    private readonly RequestDelegate next;

    public ClaimsMiddleware(RequestDelegate next)
    {
        this.next = next ?? throw new ArgumentNullException(nameof(next));
    }

    public async Task Invoke(HttpContext httpContext)
    {
        if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));

        // Get values from claims here
        var myVal = httpContext
                .User
                .Claims
                .Where(x => x.Type == "MyVal")
                .Select(x => x.Value)
                .DefaultIfEmpty(string.Empty)
                .SingleOrDefault();

        using (LogContext.PushProperty("MyVal", myVal))
        {
            try
            {
                await next(httpContext);
            }

            // Never caught, because `LogException()` returns false.
            catch (Exception ex) when (LogException(httpContext, ex)) { }
        }
    }

    private static bool LogException(HttpContext httpContext, Exception ex)
    {
        var logForContext = Log.ForContext("StackTrace", ex.StackTrace);

        logForContext.Error(ex, ex.Message);

        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

tsi*_*lar 6

更新 我想你可能想看看这篇文章:http://mylifeforthecode.github.io/enriching-serilog-output-with-httpcontext-information-in-asp-net-core/

这个想法是注册一个自定义中间件,它将在请求期间将所有上下文信息添加到当前。LogContext

为了让它工作,你必须配置你的记录器

Log.Logger = new LoggerConfiguration()
      // snip ....MinimumLevel.Debug()
      .Enrich.FromLogContext()                
      // snip ...
.CreateLogger(); 
Run Code Online (Sandbox Code Playgroud)

Nicholas Blumhardt 的这篇文章也可能有所帮助:https ://blog.getseq.net/smart-logging-middleware-for-asp-net-core/


警告 - 以下解决方案在这种情况下不起作用

如果记录器提前注册(在 Program.Main() 中),下面的解决方案将无法工作

首先,如果您想添加附加到记录的事件的额外信息,我相信您想要的是Enricher

然后你可以:

  • 将IHttpContextAccessor注册到您的 ServiceCollection 中(例如,使用AddHttpContextAccessor()):services.AddHttpContextAccessor();
  • 创建一个在其构造函数中ILogEventEnricher接受的实现IHttpContextAccessor
  • 配置记录器时,注入IHttpContextAccessor(通过将类型参数添加IHttpContextAccessorStartup.Configure()
  • 将此丰富器添加到您的记录器中

丰富可能看起来像https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/ClaimValueEnricher.cs

你可以像这样配置你的记录器:

var logger = new LoggerConfiguration()
                .EnrichWith(new MyEnricher(contextAccessor))
                .WriteTo.Whatever()
                .CreateLogger();
Run Code Online (Sandbox Code Playgroud)