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

时间:2019-03-18 14:32:10

标签: c# asp.net-mvc asp.net-core error-handling serilog

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

主要目的是将日志记录系统与异常处理程序中间件相结合。因此,在异常处理程序中捕获了错误,该错误是从控制器方法引发的。我将_logger.Log()放在异常处理程序中的任何位置,接收器中都没有自定义属性。调试时,它先经过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?}");
        });
    }

LogContextFilter

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

ExceptionHandlerMiddleware

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

2 个答案:

答案 0 :(得分:2)

之所以发生这种情况,是因为该异常记录在运行在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);

前一段时间,我研究了这个问题并写了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);

答案 1 :(得分:0)

我也为此付出了努力,几个月前找到了答案(虽然现在找不到。寻找它,这就是我偶然发现您的问题的方式。)可以肯定的是,您现在已经找到了解决方案,但这可能会对某人有所帮助。

但是尝试这种变化:

catch (Exception ex2) when (LogUnexpectedError(ex2))
{
    // Suppress secondary exceptions, re-throw the original.        
    context.Request.Path = originalPath;
    throw ex;
}

private bool LogUnexpectedError(Exception ex)
{
    Log.Error(ex.Message);
    return true;
}

如果我没有记错的话,那么在LogExceptionFilter超出范围之前,when部分是唯一可以处理Exception的地方。希望对您有所帮助。

更新:找到了我最初发现它的位置: https://andrewlock.net/how-to-include-scopes-when-logging-exceptions-in-asp-net-core/#using-exception-filters-to-capture-scopes