Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add HttpContext to enrich un-handled exception logs?

I have setup Serilog to log to MSSql using:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
    .MinimumLevel.Override("System", LogEventLevel.Information)
    .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
    .Enrich.FromLogContext()
    .WriteTo.Async(x => x.MSSqlServer(logConntectionString, tableName, LogEventLevel.Warning, autoCreateSqlTable: false, columnOptions: columnOptions))
    .CreateLogger();

Additionally I have added added a SerilogMiddleware in the pipeline that successfully adds LogContext from the HttpContext.

In a test controller, I have these 2 test methods:

public class TestController : ControllerBase
{
    [HttpGet, Route("test")]
    public IActionResult Get() {
        try
        {
            string[] sar = new string[0];                
            var errorgenerator = sar[2]; // Trigger exception
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Caught Exception");
            return StatusCode(500, "Custom 500 Error");
        }
        return Ok();
    }

    [HttpGet, Route("test2")]
    public IActionResult Get2() {

        string[] sar = new string[0];
        var errorgenerator = sar[2];// Trigger exception

        return Ok();
    }
}

The first method is not DRY, and so I would like to handle global/uncaught exceptions such as method 2.

What I have from here is:

public class GloablExceptionFilter : ActionFilterAttribute, IExceptionFilter
{    
    public void OnException(ExceptionContext context)
    {
        var httpContext = context.HttpContext;  //  This does not appear to have the actual HttpContext

        Log.Error(context.Exception, "Unhandled Exception");
    }
}

Problem is, my middleware that otherwise worked no longer does.. It does not edit the response body, etc... Further, when I access ExceptionContext's context.HttpContext, it does not contain the actual HttpContext when triggered from inside a controller method such as above.

  1. How do I inject or share HttpContext and or LogContext with this Filter?
  2. If thats not possible, how do I accomplish logging exceptions, while being DRY, and having context when its available?

Update 1: Current Middleware

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddSerilog();            

    app.UseAuthentication();

    // Logging Middleware is just after Authentication, to have access to 
    // user IsAuthorized, claims, etc..
    app.UseMiddleware<SerilogMiddleware>();

    app.UseCors("CORSPolicy");

    app.UseMvc();
}

In the middleware itself:

public class SerilogMiddleware
{
    readonly RequestDelegate _next;

    public SerilogMiddleware(RequestDelegate next)
    {
        if (next == null) throw new ArgumentNullException(nameof(next));
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
      // Do logging stuff with Request..
       await _next(httpContext);
      // Do logging stuff with Response but..
      // This point is never reached, when exception is unhandled.
    }
}
like image 442
ttugates Avatar asked Dec 08 '25 21:12

ttugates


1 Answers

Based on code snippet you are not catching the exception when you pass the context down the pipeline.

If you do not catch/handle the exception within the middleware then it wont reach your code after calling down stream.

public class SerilogMiddleware {
    readonly RequestDelegate _next;

    public SerilogMiddleware(RequestDelegate next) {
        if (next == null) throw new ArgumentNullException(nameof(next));
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext) {
        // Do logging stuff with Request..
        try {
            await _next(httpContext);
        } catch(Exception ex) {
            try {
                //Do exception specific logging

                // if you don't want to rethrow the original exception
                // then call return:
                // return;
            } catch (Exception loggingException) {
                //custom
            }

            // Otherwise re -throw the original exception
            throw;
        }
        // Do logging stuff with Response      
    }
}

The above will re-throw the original error after logging it so that the other handler in the pipeline will catch it and do the out of the box handling.

like image 164
Nkosi Avatar answered Dec 12 '25 02:12

Nkosi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!