Reputation: 843
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:
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:
Please tell me how you deal with this.
Upvotes: 7
Views: 5441
Reputation: 25158
I would suggest to create FormattableException and use FormattableString inside as StructuredMessage property. For backward compatibility save formatted message to Message property
Upvotes: 0
Reputation: 21057
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.
Upvotes: 2