I'm using the simple ASP.NET
provided logger that I get via dependency injection: Microsoft.Extensions.Logging.ILogger<T>
. In practice the dynamic type is Microsoft.Extensions.Logging.Logger<T>
.
When catching exceptions I'm trying to log them using: _logger.LogError(exception, "message")
, however only the message is printed.
namespace App
{
public class App : IApp
{
private readonly ILogger<App> _logger;
public PomParser(ILogger<App> logger)
=> _logger = logger;
public void DoStuff()
{
try
{
DoStuffUnsafe();
}
catch (Exception ex)
{
_logger.LogError(ex,"Failed to do stuff");
}
}
}
}
How I configure the logging:
var host = new HostBuilder().ConfigureLogging(ConfigureLogging)...
...
await host.RunAsync();
private static void ConfigureLogging(HostBuilderContext hostContext, ILoggingBuilder configLogging)
{
configLogging.ClearProviders();
configLogging.AddConfiguration(hostContext.Configuration.GetSection("Logging"));
configLogging.AddFile(
options =>
{
hostContext.Configuration.GetSection("FileLoggingOptions")
.Bind(options);
}
);
configLogging.AddConsoleLogger();
}
appsettings:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"System": "Information",
"Microsoft": "Information"
}
},
"FileLoggingOptions": {
"FileName": "app-",
"LogDirectory": "logs",
"FileSizeLimit": 10485760,
"Extension": "log"
}
}
If your logger isn't logging the exception it could also be because you accidentally got the order of the passed arguments wrong:
_logger.LogError("An error occurred", e) // WRONG: Exception will not be logged
The correct order is providing the exception object always as the first argument:
_logger.LogError(e, "An error occurred") // OK: Will log the exception
References:
https://blog.rsuter.com/logging-with-ilogger-recommendations-and-best-practices/#always-pass-exception-as-first-parameter
See the default MessageFormatter
: https://github.com/aspnet/Logging/blob/master/src/Microsoft.Extensions.Logging.Abstractions/LoggerExtensions.cs
private static string MessageFormatter(FormattedLogValues state, Exception error)
{
return state.ToString();
}
It simply ignores the exception ... I implemented a custom Console logger:
public class ConsoleLoggerProvider : ILoggerProvider
{
public void Dispose()
{
}
public ILogger CreateLogger(string categoryName)
=> new ConsoleLogger(categoryName);
private class ConsoleLogger : ILogger
{
private readonly string _categoryName;
public ConsoleLogger(string categoryName)
=> _categoryName = categoryName;
public void Log<TState>(
LogLevel logLevel, EventId eventId, TState state, Exception exception,
Func<TState, Exception, string> formatter
)
{
if (!IsEnabled(logLevel))
{
return;
}
Console.WriteLine(
$"{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} [{logLevel}] {_categoryName}: {state}{(exception != null ? "\n" : string.Empty)}{exception}"
);
}
public bool IsEnabled(LogLevel logLevel)
=> true;
public IDisposable BeginScope<TState>(TState state)
=> null;
}
}
And to use it:
public static Task Main(string[] args)
=> WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(...)
.ConfigureServices(...)
.ConfigureLogging((hostContext, loggingBuilder) => loggingBuilder.AddProvider(new ConsoleLoggerProvider()))
.UseStartup<Startup>()
.Build()
.RunAsyncSafe();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With