简体   繁体   English

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

[英]Serilog Logcontext properties are gone after exception handler

In my website I'm integrating Serilog to log my errors to a custom sink.在我的网站中,我正在集成 Serilog 以将我的错误记录到自定义接收器中。 The logging is enriched with a LogContext where some custom properties needs to be passed. LogContext 丰富了日志记录,其中需要传递一些自定义属性。 If I use Log.Information() it arrives at my sink with the properties in the LogEvent.如果我使用 Log.Information(),它会使用 LogEvent 中的属性到达我的接收器。 So this is working great.所以这很好用。

The main purpose is to combine the logging system to a exception handler middleware.主要目的是将日志系统与异常处理程序中间件结合起来。 So in the exception handler the error is caught, which is thrown from a controller method.所以在异常处理程序中,错误被捕获,这是从控制器方法抛出的。 Anywhere I place the _logger.Log() in the exception handler, no custom properties are available in the Sink.我将 _logger.Log() 放在异常处理程序中的任何地方,Sink 中都没有可用的自定义属性。 While debugging it passes the LogContextFilter before it goes to the Sink, but no properties of the filter are found.在调试时,它在进入 Sink 之前通过了 LogContextFilter,但没有找到过滤器的属性。

Does anyone as any idea?有没有人有任何想法?

Startup启动

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

This happens because the exception gets logged in a handler that runs outside of using (LogContext.Push(..)) , therefore custom properties already have gone from context.发生这种情况是因为异常被记录在一个在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);

Some time ago I researched this problem and wrote ThrowContextEnricher .前段时间我研究了这个问题,写了ThrowContextEnricher

This library captures context from a point where an exception was thrown.该库从抛出异常的点捕获上下文。 Then ThrowContextEnricher can be used to enrich the exception log with the original context.然后可以使用 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);

I struggled with this also and found the answer a few months back (can't find it now though. Searching for it and that's how I stumbled on your question.).我也为此苦苦挣扎,并在几个月前找到了答案(虽然现在找不到。搜索它,这就是我偶然发现您的问题的方式。)。 Pretty sure you found a solution by now but this might help someone.很确定您现在找到了解决方案,但这可能会对某人有所帮助。

But try this variation:但是试试这个变体:

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

If I remember correctly the when part is the only place where you can handle the Exception before the LogExceptionFilter goes out of scope.如果我没记错的话, when 部分是您可以在 LogExceptionFilter 超出范围之前处理异常的唯一地方。 Hope it helps.希望能帮助到你。

Update: Found where I found this originally: https://andrewlock.net/how-to-include-scopes-when-logging-exceptions-in-asp-net-core/#using-exception-filters-to-capture-scopes更新:找到我最初发现的地方: https : //andrewlock.net/how-to-include-scopes-when-logging-exceptions-in-asp-net-core/#using-exception-filters-to-capture-范围

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM