'How to preserve log scopes for unhandled exceptions?

Say there's a function that uses a logger with scopes so that:

public void HttpFunction(ILogger log)
{
  using log.BeginScope("somescope");
  FunctionA(); // this can throw an exception
}

If FunctionA throws an exception, somescope won't be added to the exception that the logger automatically logs because the control will be outside the function block and the scope will be disposed off.

Is there a way to automatically have the logger log the exception, along with the added scope? I prefer not having a try catch block on FunctionA just for logging it out (and also the fact that if a developer later adds a FunctionB, they can make a mistake and forget to wrap it in a try-catch). Also, the catch block I would add would "handle" the error by doing a log-error-and-return-500, stuff that automatically happens anyways.

Also, I'm using Serilog, but I don't think that's relevant.



Solution 1:[1]

Yes, there's a trick for this, it's implemented in Serilog's own request logging middleware.

In your outermost exception handler, log the exception inside the when clause instead of in the catch block:

try
{
    function();
}
catch (Exception ex) when (LogException(ex))
{
}

Where LogException() can be something like:

static bool LogException(Exception ex)
{
    Log.Error(ex, "Unhandled excpetion");
    return false; // Don't actually catch the exception
}

Exception filters (when clauses) are special - they execute in the context in which the exception is thrown, rather than in the context that it's caught, so the LogContext is still available at that point.

Solution 2:[2]

You could try to use Activity.Current?.AddTag instead of log.BeginScope and Serilog.Enrichers.ActivityTags

Something like

configureHostBuilder.UseSerilog((context, loggerConfiguration) =>
{
    loggerConfiguration
        .Enrich.WithActivityTags()
        ...
});

and

public void HttpFunction(ILogger log)
{
    Activity.Current?.AddTag("somescope", "scopevalue");
    FunctionA(); // this can throw an exception
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 askazakov