Serilog Logcontext 属性在异常处理程序之后消失了

NiA*_*iAu 4 c# error-handling asp.net-mvc serilog asp.net-core

在我的网站中,我正在集成 Serilog 以将我的错误记录到自定义接收器中。LogContext 丰富了日志记录,其中需要传递一些自定义属性。如果我使用 Log.Information(),它会使用 LogEvent 中的属性到达我的接收器。所以这很好用。

主要目的是将日志系统与异常处理程序中间件结合起来。所以在异常处理程序中,错误被捕获,这是从控制器方法抛出的。我将 _logger.Log() 放在异常处理程序中的任何地方,Sink 中都没有可用的自定义属性。在调试时,它在进入 Sink 之前通过了 LogContextFilter,但没有找到过滤器的属性。

有没有人有任何想法?

启动

Log.Logger = new LoggerConfiguration()
            .WriteTo.PasSink(new SerLogServiceClient.SerLogServiceClient(new SerLogServiceClientOptions()))
            .Enrich.FromLogContext()
            .CreateLogger();

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddMvcOptions(mo =>
        {
            mo.Filters.Add(typeof(LogContextFilter));
        });

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMiddleware<LogContextMiddleware>();
        app.UseErrorHandler(o =>
        {
            o.ExceptionHandlingPath = "/Home/Error";
            o.Context = ExceptionHandler.Context.MVC;
        });

        //app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseStaticFiles(new StaticFileOptions
        {
            FileProvider = new PhysicalFileProvider(
                Path.Combine(Directory.GetCurrentDirectory(), "Content")),
            RequestPath = "/Content"
        });

        app.UseAuthentication();

        app.UseSession();
        //app.UseCookiePolicy();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
Run Code Online (Sandbox Code Playgroud)

日志上下文过滤器

public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        using (LogContext.Push(
            new PropertyEnricher("UserCode", context.HttpContext.User.Claims.FirstOrDefault(s => s.ToString().StartsWith("UserCode"))?.Value),
            new PropertyEnricher("Test", "Will this go through?")))
        {
            await next.Invoke();
        }
    }
Run Code Online (Sandbox Code Playgroud)

ExceptionHandler 中间件

public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next.Invoke(context);
        }
        catch (HttpRequestException hex)
        {
            //check response naar reynaersexception??
            //deserialize naar re
            throw new NotSupportedException();  //als test
        }
        catch  (Exception ex)
        {

            if (context.Response.HasStarted)
            {
                throw ex;
            }

            _logger.LogError(ex.Message);

            var originalPath = context.Request.Path;
            try
            {
                if (_options.Context == Context.MVC)
                {
                    context.Response.Clear();
                    context.Response.StatusCode = 500;
                    context.Response.OnStarting(Callback, context.Response);

                    //set features
                    var exceptionHandlerFeature = new ReynaersExceptionHandlerFeature()
                    {
                        Error = ex,
                        Path = context.Request.Path.Value,
                    };
                    context.Features.Set<IExceptionHandlerFeature>(exceptionHandlerFeature);
                    context.Features.Set<IExceptionHandlerPathFeature>(exceptionHandlerFeature);

                    //continue lifecycle with updated context
                    if (_options.ExceptionHandlingPath.HasValue)
                    {
                        context.Request.Path = _options.ExceptionHandlingPath;
                    }

                    await _next.Invoke(context);
                }
            }
            catch (Exception ex2)
            {
                // Suppress secondary exceptions, re-throw the original.
                Log.Error(ex2.Message);
                context.Request.Path = originalPath;
                throw ex;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

小智 6

发生这种情况是因为异常记录在运行于 之外的处理程序中using (LogContext.Push(..)),因此自定义属性已经从上下文中消失。

...

// in mvc's OnActionExecutionAsync()
        using (LogContext.Push(
            new PropertyEnricher("UserCode", ".."),
            new PropertyEnricher("Test", "Will this go through?")))
        {
            await next.Invoke(); // code that throws
        }

...

// later in ExceptionHandlerMiddleware, no custom properties
_logger.LogError(ex.Message);
Run Code Online (Sandbox Code Playgroud)

前段时间我研究了这个问题,写了ThrowContextEnricher

该库从抛出异常的点捕获上下文。然后可以使用 ThrowContextEnricher 用原始上下文来丰富异常日志。

Log.Logger = new LoggerConfiguration()
    .Enrich.With<ThrowContextEnricher>()  // Adds enricher globally
    .Enrich.FromLogContext()
    .WriteTo
    ...
    .CreateLogger();
...


// in mvc's OnActionExecutionAsync()
// push your properties as normal
        using (LogContext.Push(
            new PropertyEnricher("UserCode", ".."),
            new PropertyEnricher("Test", "Will this go through?")))
        {
            await next.Invoke(); // code that throws
        }

...

// in exception handler
// properties get logged now
// notice the exception is passed too, not just message
_logger.LogError(ex, ex.Message);

Run Code Online (Sandbox Code Playgroud)