.NET 6新特性试用 | 自动生成高性能日志记录代码
前言
要想記錄日志,常用的方式是訪問(wèn)ILogger實(shí)例提供的日志記錄方法:
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; }其實(shí),.NET下還有一個(gè)高性能日志記錄類LoggerMessage[1]。
與ILogger記錄器擴(kuò)展方法(例如LogInformation和LogDebug)相比,LoggerMessage具有以下性能優(yōu)勢(shì):
記錄器擴(kuò)展方法需要將值類型(例如 int)“裝箱”(轉(zhuǎn)換)到 object中。LoggerMessage模式使用帶強(qiáng)類型參數(shù)的靜態(tài)Action字段和擴(kuò)展方法來(lái)避免裝箱。
記錄器擴(kuò)展方法每次寫入日志消息時(shí)必須分析消息模板(命名的格式字符串)。如果已定義消息,那么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消息模板中的參數(shù)占位符并沒有任何控制(例如{aa}),很可能導(dǎo)致傳遞錯(cuò)誤參數(shù)。
而在.NET 6中,可以使用Source Generator幫助我們自動(dòng)生成高性能日志記錄代碼。
Demo
你需要?jiǎng)?chuàng)建一個(gè)partial方法,然后在其頭部聲明LoggerMessageAttribute。
示例代碼如下:
[LoggerMessage(0,?LogLevel.Information,?"LoggerMessageAttribute:?{weatherForecasts}")] partial?void?LogWeatherForecast(IEnumerable<WeatherForecast>?weatherForecasts);//使用 LogWeatherForecast(result);查看自動(dòng)生成的代碼,其實(shí)是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對(duì)象,并不需要我們傳入;而且寫入日志前判斷了_logger.IsEnabled避免不必要的日志寫入操作,對(duì)性能有進(jìn)一步提高。
更為重要的是,它不會(huì)允許傳入錯(cuò)誤的參數(shù):
結(jié)論
使用LoggerMessageAttribute可以提高日志記錄性能,但它也有其缺點(diǎn):
使用partial方法聲明必須將類也定義成partial。
日志使用了參數(shù)對(duì)象的ToString()方法,對(duì)于復(fù)雜類型,不能在方法中傳入序列化對(duì)象LogWeatherForecast(JsonSerializer.Serialize(result)),因?yàn)闀?huì)始終執(zhí)行影響性能,可以通過(guò)定義成record class或自定義ToString()方法變通解決:
參考資料
[1]
LoggerMessage: https://docs.microsoft.com/zh-cn/dotnet/core/extensions/high-performance-logging
如果你覺得這篇文章對(duì)你有所啟發(fā),請(qǐng)關(guān)注我的個(gè)人公眾號(hào)”My IO“
總結(jié)
以上是生活随笔為你收集整理的.NET 6新特性试用 | 自动生成高性能日志记录代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何使用cURL获得请求和响应时间?
- 下一篇: 在.Net环境下使用elasticsea