Steve Steve - 3 months ago 48
C# Question

Bubble up exception with custom properties and log with serilog

I came across a situation where I thought it'd be good to rethrow an exception, appending additional information to it (for example in properties in a custom exception) that when caught further up the stack would be logged with the additional info in the form of properties of a message template in Serilog - the end goal being so I can then filter on those properties in Seq. This scenario might be a warning that I need to rethink my exception handling, but I thought I'd ask if this has been done before with Serilog? Recommended/discouraged?

Update

Here's an example scenario:

Using serilog and Seq we apply a "Department" property to a log so that a department can easily see all exceptions that are likely their responsibility.

Now the app for this scenario basically does two things:


  • Step 1: Query data.

  • Step 2: If no exceptions in step 1, post it somewhere else.



A few layers deep in step 1 there is a particular exception that I know is the responsibility of a particular department. So I'd like to apply a department property to this exception when it is logged but at the same time have it be caught further up the stack so that the exception prevents continuation of everything else. Also, I only want to log the exception once.

My current approach is to define a custom exception with a dictionary that can hold properties for the log:

public class ExtPropertiesException : Exception
{
// Constructors here...

/// <summary>
/// Properties to log with the error message
/// </summary>
public Dictionary<string, object> ExtProperties { get; set; }
}


So when I catch an exception I know is a particular departments responsibility I rethrow it as ExtPropertiesException or as an exception that inherits it, attaching the original exception as the inner exception.

Then back up the stack where the general steps are I've got a catch:

try
{
// Step 1
// Step 2
}
catch (ExtPropertiesException ex)
{
if (ex.ExtProperties != null)
{
foreach (var prop in ex.ExtProperties)
{
logger = logger.ForContext(prop.Key, prop.Value);
}
}
logger.Error(ex, ex.Message);
}


This seems to be doing what I need, but it'd be great to know whether it's a best practice before repeating it elsewhere.

I was initially doing the logs and applying the property at the time I catch the exception and then just returning false or null to indicate failure and abandon future steps, however I've found it difficult for unit testing as I can't determine the type of errors from outside the methods.

Thanks

Answer

Best practice is hard to judge; rather than the ForContext() code above you can do:

try
{
    // Step 1
    // Step 2
}
catch (ExtPropertiesException ex)
{
    logger.Error(ex, "Exception caught, data is {@ExtProperties}", ex.ExtProperties);
}

This will attach a single "object" property to the event, carrying the data from ExtProperties.

Comments