If you mention logging in .NET, you will definitely think of ILogger, which is a common way to provide logging in .NET. The following code is the code for the initialization of the .NET Core WebAPI project, which uses ILogger to provide logging Logging.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
    _logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{ 
    var result =  Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        TemperatureC = Random.Shared.Next(-20, 55),
    })
    .ToArray();
    _logger.LogInformation("LogInformation: {0}", JsonSerializer.Serialize(result));
    return result;
}

NET6 Microsoft provides us with a high-performance logging class LoggerMessage. Compared to the ILogger logger and its extension methods, LoggerMessage has performance advantages. First, the ILogger logger extension methods require value type conversion to object, but LoggerMessage uses static methods with strongly typed parameters and extension methods to avoid this problem. Also, the ILogger logger and its extension methods must first parse the message template each time the log is written, but LoggerMessage only needs to parse the template once if the message template is already defined. The code used is as follows (modifying the WebAPI project initialization code)

1
2
3
4
5
6
private static readonly Action<ILogger, IEnumerable<WeatherForecast>, Exception?> _logWeatherForecast =
    LoggerMessage.Define<IEnumerable<WeatherForecast>>(
        logLevel: LogLevel.Information,
        eventId: 0,
        formatString: "LoggerMessage: {aa}");
_logWeatherForecast(_logger, result, null);

Although LoggerMessage provides us with better performance logging, it requires a lot of manual writing of LoggerMessage.Define code, and the parameter placeholders in the formatString message template are not controlled in any way, which may lead to incorrect passing of parameters. NET 6 Microsoft provides Source Generator to help us automatically generate high-performance logging code. It is very simple to use, first you need to create partial method and second declare LoggerMessageAttribute attribute in the partial method header. The code used is as follows.

1
2
3
4
[LoggerMessage(0, LogLevel.Information, "LoggerMessageAttribute: {weatherForecasts}")]
partial void LogWeatherForecast(IEnumerable<WeatherForecast> weatherForecasts);
//使用
LogWeatherForecast(result);

Once compiled, LoggerMessage automatically generates the code for us.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
partial class WeatherForecastController 
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.5.2210")]
    private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, global::System.Collections.Generic.IEnumerable<global::WebApplication1.WeatherForecast>, global::System.Exception?> __LogWeatherForecastCallback =
        global::Microsoft.Extensions.Logging.LoggerMessage.Define<global::System.Collections.Generic.IEnumerable<global::WebApplication1.WeatherForecast>>(global::Microsoft.Extensions.Logging.LogLevel.Information, new global::Microsoft.Extensions.Logging.EventId(0, nameof(LogWeatherForecast)), "LoggerMessageAttribute: {weatherForecasts}", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true });     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.5.2210")]
    partial void LogWeatherForecast(global::System.Collections.Generic.IEnumerable<global::WebApplication1.WeatherForecast> weatherForecasts)
    {
        if (_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Information))
        {
            __LogWeatherForecastCallback(_logger, weatherForecasts, null);
        }
    }
}

In the above code, the LogWeatherForecast method directly uses the _logger object declared in the Controller without us passing it in, and also determines _logger.IsEnabled before writing logs, which avoids unnecessary log writing and further improves performance. While using LoggerMessageAttribute can improve logging performance, it also has its disadvantages.

  1. using partial method declarations requires that the class also be defined as partial. 2.Logging uses the ToString() method of the parameter object, and for complex types you cannot pass a serialized object into the method LogWeatherForecast(JsonSerializer.Serialize(result)) because it will always be executed and affect the performance, but you can define it as a record class or Custom ToString() method workaround.