我正在使用Building a Simple Sink示例创建实现ILogEventSink的自己的SeriLog接收器,目的是记录用户声明中的某些信息。为了访问Core中的HttpContext,我通常会注入(inject)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));
    }
}

...然后使用水槽...
var log = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.MySink()
    .CreateLogger();

如何在接收器的Emit方法中访问当前的HttpContext?还是可以通过DI框架创建接收器?

我有一个MVC站点,使用Serilog.AspNetCore v2.1.0针对.Net 4.6.2运行时运行Asp.Net Core 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;
    }
}

最佳答案

更新
我想您可能想看一下这篇文章: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();

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

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

如果记录程序过早注册(在Program.Main()中),则下面的解决方案将不起作用

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

然后,您可以:
  • IHttpContextAccessor注册到您的ServiceCollection中(例如,使用 AddHttpContextAccessor() ):services.AddHttpContextAccessor();
  • 创建ILogEventEnricher的实现,该实现在其构造函数
  • 中接受IHttpContextAccessor
  • 配置记录器时,注入(inject)IHttpContextAccessor(通过将IHttpContextAccessor类型的参数添加到Startup.Configure()
  • 将此浓缩器添加到记录器中

  • 增强程序可能类似于https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/ClaimValueEnricher.cs

    您可以像这样配置logger的:
    var logger = new LoggerConfiguration()
                    .EnrichWith(new MyEnricher(contextAccessor))
                    .WriteTo.Whatever()
                    .CreateLogger();
    

    关于c# - 如何在SeriLog接收器中获取当前的HttpContext?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47071487/

    10-13 04:31