.NET 6新特性试用 | 自动生成高性能日志记录代码
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET 6新特性试用 | 自动生成高性能日志记录代码相关的知识,希望对你有一定的参考价值。
前言
要想记录日志,常用的方式是访问ILogger实例提供的日志记录方法:
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;
其实,.NET下还有一个高性能日志记录类LoggerMessage[1]。
与ILogger记录器扩展方法(例如LogInformation和LogDebug)相比,LoggerMessage具有以下性能优势:
记录器扩展方法需要将值类型(例如 int)“装箱”(转换)到 object中。LoggerMessage模式使用带强类型参数的静态Action字段和扩展方法来避免装箱。
记录器扩展方法每次写入日志消息时必须分析消息模板(命名的格式字符串)。如果已定义消息,那么LoggerMessage只需分析一次模板即可。
示例代码如下:
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);
虽然使用LoggerMessage可以为我们提供更好的性能,但是,需要手工编写大量的LoggerMessage.Define代码;而且formatString消息模板中的参数占位符并没有任何控制(例如aa
),很可能导致传递错误参数。
而在.NET 6中,可以使用Source Generator帮助我们自动生成高性能日志记录代码。
Demo
你需要创建一个partial
方法,然后在其头部声明LoggerMessageAttribute
。
示例代码如下:
[LoggerMessage(0, LogLevel.Information, "LoggerMessageAttribute: weatherForecasts")]
partial void LogWeatherForecast(IEnumerable<WeatherForecast> weatherForecasts);
//使用
LogWeatherForecast(result);
查看自动生成的代码,其实是Source Generator帮我们编写了LoggerMessage.Define代码:
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);
LogWeatherForecast
方法直接使用了Controller中声明的_logger对象,并不需要我们传入;而且写入日志前判断了_logger.IsEnabled
避免不必要的日志写入操作,对性能有进一步提高。
更为重要的是,它不会允许传入错误的参数:
结论
使用LoggerMessageAttribute
可以提高日志记录性能,但它也有其缺点:
使用
partial
方法声明必须将类也定义成partial
。日志使用了参数对象的ToString()方法,对于复杂类型,不能在方法中传入序列化对象
LogWeatherForecast(JsonSerializer.Serialize(result))
,因为会始终执行影响性能,可以通过定义成record class
或自定义ToString()方法变通解决:
参考资料
[1]
LoggerMessage: https://docs.microsoft.com/zh-cn/dotnet/core/extensions/high-performance-logging
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“
以上是关于.NET 6新特性试用 | 自动生成高性能日志记录代码的主要内容,如果未能解决你的问题,请参考以下文章