简体   繁体   中英

How to log exceptions with structured arguments in .NET Core

I can't find a consistent way to log exceptions in my .NET Core microservice. Informational messages logging guidelines are simple (Microsoft.Extension.Logging is used):

_logger.LogInformation($"Reading file {path}..."); // bad
_logger.LogInformation("Reading file {Path}...", path); // good

The benefit of the second variant is structured information: by using a clever log event router (like Serilog with RenderedCompactJsonFormatter) the path is written as separate property of the log event.

Things are going worse with errors logging. The requirements are obvious:

  1. Errors are implemented as exceptions.
  2. An error is logged in the catch block where it is handled.
  3. Each error is logged in a structured form.

So, I'd expect the error reporting to look like

throw new MyException("Failed to read file {Path}", path);

and error logging - like

catch(MyException e)
{
  _logger.LogError(e, "Processing error");
}

LogError method here logs the complete error description, but it is not structured: the path is not added as a property. I tried to make MyException hold the message template and the arguments, but there are 2 problems with this approach:

  1. How to render the exception message based on a template with named arguments?
  2. An argument may be disposed when an exception is processed in a catch block.

Please tell me how you deal with this.

Exceptions in .NET don't support structured parameters like that. Since you are using custom exceptions, you could add this functionality to your own exception class.

For example:

public class MyException : Exception
{
    public object[] Props { get; }

    public MyException()
    {
    }

    public MyException(string message)
        : base(message)
    {
    }

    // Add custom "props" parameter to your exception class
    public MyException(string message, params object[] props)
        : base(message)
    {
        Props = props;
    }

    public MyException(string message, Exception inner)
        : base(message, inner)
    {
    }
}

Now in your code you could do something like this:

try
{
    var file = "image.png";
    var path = "./my/path/";
    throw new MyException("Failed to read file '{file}' in path '{path}'", file, path);
}
catch (MyException e)
{
    _logger.LogError(e, e.Message, e.Props);
}

If you look at your logs (I'm using InvisionLog here), then you should see that it is structured.

I think this is the only way to catch your exceptions and log them in a structured manner.

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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