.NET Core之文件日志记录库对比(Serilog、log4net、NLog)
一、前言
在应用程序的开发,部署阶段,我们都会对调试、运行中的相关日志进行记录,包括Debug日志、异常日志、对接日志等。日志管理提供运行问题的记录,在对问题进行定位分析时候提供重要的作用,没有日志就不能定位问题具体在代码所在行,具体信息,具体产生的时间,或者更详细的进程、线程内容,所以每一个应用程序都必须具备日志管理的功能。在没有使用开源组件前,一般直接通过文件流,写日志帮助类将日志记录文件中,但是要考虑文件写入锁,异步,文件大小限制,日志结构等问题处理。面对这个基础需求,提供了很多开源组件,解决这个问题,通过下述三个组件(log4net、nlog、Serilog)实践来对比,总结.NET Core的日志文件记录功能。
二、组件介绍
1、关于log4net
定义:log4net是将出色的Apache log4(JAVA)框架移植到.NET运行时环境的产品,在.NET Framework时代是常用的日志管理组件,通过XML配置方式提供强大的灵活性,日志记录策略、日志记录结构等完全的控制。
原文:The Apache log4net library is a tool to help the programmer output log statements to a variety of output targets. log4net is a port of the excellent Apache log4j™ framework to the Microsoft® .NET runtime. We have kept the framework similar in spirit to the original log4j while taking advantage of new features in the .NET runtime. For more information on log4net see the features document.
地址:log4net的官网https://logging.apache.org/log4net/,开源项目https://github.com/apache/logging-log4net/tree/master。
2、关于nlog
定义:NLog是一个灵活且免费的日志记录平台,适用于包括.NET标准在内的各种.NET平台。NLog可以轻松地将日志写入多个目标(数据库、文件、控制台),并实时更改日志配置。NLog支持结构化和传统日志记录。NLog的主要关注点在于:高性能、易于使用、易于扩展以及配置灵活。
原文:NLog is a flexible and free logging platform for various .NET platforms, including .NET standard. NLog makes it easy to write to several targets. (database, file, console) and change the logging configuration on-the-fly.
地址:nlog的官网https://nlog-project.org/,开源项目https://github.com/NLog/NLog.Extensions.Logging。
3、关于Serilog
定义:Serilog是为.NET应用提供诊断日志的类库,提供容易的配置,干净的接口,满足全部的.NET平台(支持的平台是.NET/.NET Core,.NET Framework 4.5 +,Windows(8 / WinRT / Universal +)和Windows Phone 8 +)。在简单应用程序中提供很大作用,同样在复杂的,分布式,异步的系统中提供结构化日志的支持。
原文:Serilog is a diagnostic logging library for .NET applications. It is easy to set up, has a clean API, and runs on all recent .NET platforms. While it's useful even in the simplest applications, Serilog's support for structured logging shines when instrumenting complex, distributed, and asynchronous applications and systems.
地址:Serilog官网https://serilog.net,开源项目https://github.com/serilog/serilog。
三、Demo实践
在ASP.NET Core中,通过上述三个组件实现一个文件日志记录功能,要求按照日志等级记录在不同的文件夹中,按照日期生成.txt文件,限制文件大小,运行一个日期多个文件,最好提供一个结构化的日志内容,并且使用ASP.NET Core的Microsoft.Extensions.Logging写入日志。基于上述的需求,使用上述组件从①创建ASP.NET Core项目、②引入在ASP.NET Core需要类库包、③配置xml、config、appsetting等配置项、④program中注入日志服务或者启用中间件、⑤代码中使用ASP.NET Core的日志logging对象写入日志。
1、实践log4net
第一步、在tqf.log4net.Demo(.net 8.0)中选择要安装的包文件,主要log4net,支持ASP.NET Core的Microsoft.Extensions.Logging.Log4Net.AspNetCore。
<PackageReference Include="log4net" Version="2.0.17" /> <PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="8.0.0" />
第二步、在tqf.log4net.Demo中新增config文件,命名log4net.config,主要配置项是设置各个日志等级,存储的文件路径,文件最大Size,文件名称格式,日志内容格式,并且支持数据库存储的方式。
<?xml version="1.0" encoding="utf-8"?> <log4net> <root> <priority value="ALL" /> <appender-ref ref="RollingLogFileAppender" /> <appender-ref ref="RollingLogWarnFileAppender"/> <appender-ref ref="RollingLogFatalFileAppender"/> <appender-ref ref="RollingLogInfoFileAppender"/> <!--<appender-ref ref="ColoredConsoleAppender" />--> </root> <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="log\Error\" /> <param name="AppendToFile" value="true" /> <param name="MaxFileSize" value="10240" /> <param name="MaxSizeRollBackups" value="100" /> <param name="StaticLogFileName" value="false" /> <param name="DatePattern" value="yyyy_MM_dd'.log'" /> <param name="RollingStyle" value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%r %date [%thread] %-5level- %message%newline" /> </layout> <filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="ERROR" /> <levelMax value="ERROR" /> </filter> </appender> <appender name="RollingLogWarnFileAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="log\Warn\" /> <param name="AppendToFile" value="true" /> <param name="MaxFileSize" value="10240" /> <param name="MaxSizeRollBackups" value="100" /> <param name="StaticLogFileName" value="false" /> <param name="DatePattern" value="yyyy_MM_dd'.log'" /> <param name="RollingStyle" value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%r %date [%thread] %-5level- %message%newline" /> </layout> <filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="WARN" /> <levelMax value="WARN" /> </filter> </appender> <appender name="RollingLogFatalFileAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="log\Fatal\" /> <param name="AppendToFile" value="true" /> <param name="MaxFileSize" value="10240" /> <param name="MaxSizeRollBackups" value="100" /> <param name="StaticLogFileName" value="false" /> <param name="DatePattern" value="yyyy_MM_dd'.log'" /> <param name="RollingStyle" value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%r %date [%thread] %-5level- %message%newline" /> </layout> <filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="FATAL" /> <levelMax value="FATAL" /> </filter> </appender> <appender name="RollingLogInfoFileAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="log\Info\" /> <param name="AppendToFile" value="true" /> <param name="MaxFileSize" value="10240" /> <param name="MaxSizeRollBackups" value="100" /> <param name="StaticLogFileName" value="false" /> <param name="DatePattern" value="yyyy_MM_dd'.log'" /> <param name="RollingStyle" value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%r %date [%thread] %-5level- %message%newline" /> </layout> <filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="INFO" /> <levelMax value="INFO" /> </filter> </appender> <!--SQL数据库--> <!-- <appender name="AdoNetAppender" type="MicroKnights.Logging.AdoNetAppender, MicroKnights.Log4NetAdoNetAppender"> <bufferSize value="1" /> <connectionType value="Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient, Version=1.0.0.0,Culture=neutral,PublicKeyToken=23ec7fc2d6eaa4a5"/> <connectionString value="Integrated Security=False;Data Source=47.110.55.108;Initial Catalog=BreastHealthbat;User Id=sa;pwd=Sinoadmin@136;" /> <commandText value="INSERT INTO TLog ([ThreadId],[LogLevel],[Logger],[LogMessage],[LogException],[LogDate]) VALUES (@thread,@logLevel,@logger,@message,@exception,@logDate)" /> <parameter> <parameterName value="@thread" /> <dbType value="String" /> <size value="5" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%t" /> </layout> </parameter> <parameter> <parameterName value="@logLevel" /> <dbType value="String" /> <size value="5" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%p" /> </layout> </parameter> <parameter> <parameterName value="@logger" /> <dbType value="String" /> <size value="3000" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%logger" /> </layout> </parameter> <parameter> <parameterName value="@message" /> <dbType value="String" /> <size value="3000" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%message" /> </layout> </parameter> <parameter> <parameterName value="@exception" /> <dbType value="String" /> <size value="3000" /> <layout type="log4net.Layout.ExceptionLayout" /> </parameter> <parameter> <parameterName value="@logDate" /> <dbType value="DateTime" /> <layout type="log4net.Layout.RawTimeStampLayout" /> </parameter> </appender> --> <!--<appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender"> <mapping> <level value="FATAL" /> <foreColor value="Red, HighIntensity" /> </mapping> <mapping> <level value="ERROR" /> <foreColor value="Red" /> </mapping> <mapping> <level value="WARN" /> <foreColor value="Yellow" /> </mapping> <mapping> <level value="INFO" /> <foreColor value="White" /> </mapping> <mapping> <level value="DEBUG" /> <foreColor value="Green" /> </mapping> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%r %date [%thread] %-5level- %message%newline" /> </layout> </appender>--> </log4net>
第三步、在program中注入log4net的服务。
public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // 方式一 // builder.Logging.AddLog4Net("log4net.config"); // 方式二 builder.Services.AddLogging(logging => { logging.AddLog4Net("log4net.config"); //如果文件路径或名称有变化,需要重新设置其路径或名称 //比如在项目根目录下创建一个名为cfg的文件夹,将log4net.config文件移入其中,并改名为log.config //则需要使用下面的代码来进行配置 //logging.AddLog4Net(new Log4NetProviderOptions() //{ // Log4NetConfigFileName = "cfg/log.config", // Watch = true //}); }); var app = builder.Build(); // Configure the HTTP request pipeline. app.UseAuthorization(); app.MapControllers(); app.Run(); } }
第四步、在业务中使用日志记录,通过ASP.NET Core框架的ILogger。
using Microsoft.AspNetCore.Mvc; namespace tqf.log4net.Demo.Controllers { [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet] public IEnumerable<WeatherForecast> Get() { _logger.LogInformation("使用log4net类库记录系统日志Information"); _logger.LogWarning("使用log4net类库记录系统日志Warning"); try { throw new Exception("抛出异常,记录异常日志"); } catch (Exception ex) { _logger.LogWarning(ex.Message); } _logger.LogError("系统错误信息!"); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } } }
第五步、完成上述操作,运行程序,打印日志。
2、实践nlog
第一步、在tqf.nlog.Demo(.net 8.0)中引入包,主要包括NLog,支持ASP.NET Core的使用NLog.Web.AspNetCore。
<PackageReference Include="NLog" Version="5.3.2" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.11" />
第二步、在tqf.nlog.Demo中新增config文件,命名NLog.config,配置项主要是目标、日志等级、路由规则、规定输出内容格式等。
<?xml version="1.0" encoding="utf-8"?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" internalLogLevel="Info"> <!-- 启用.net core的核心布局渲染器 --> <extensions> <add assembly="NLog.Web.AspNetCore" /> </extensions> <!-- 写入日志的目标配置 archiveAboveSize="102400" maxArchiveDays="60" --> <targets> <!-- 调试 --> <target xsi:type="File" name="debug" fileName="../logs/debug-${shortdate}.log" layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" /> <!-- 警告 --> <target xsi:type="File" name="warn" fileName="../logs/warn-${shortdate}.log" layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" /> <!-- 错误 --> <target xsi:type="File" name="error" fileName="../logs/error-${shortdate}.log" layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" /> <!-- 控制台 --> <target xsi:type="Console" name="console" layout="${message}" /> </targets> <!-- 映射规则 --> <rules> <!-- 调试 --> <logger name="*" minlevel="Trace" maxlevel="Debug" writeTo="debug" /> <!--<logger name="*" minlevel="Trace" writeTo="console" />--> <!-- 警告 --> <logger name="*" minlevel="Info" maxlevel="Warn" writeTo="warn" /> <!-- 错误 --> <logger name="*" minlevel="Error" maxlevel="Fatal" writeTo="error" /> <!--跳过不重要的微软日志--> <logger name="Microsoft.*" maxlevel="Info" final="true" /> </rules> </nlog>
上述配置文件中的targets定义输出目标,比如Chainsaw、ColoredConsole、Console、Database、Debug、Debugger、EventLog、File、LogReceiverService、Mail、Memory、MethodCall、Network、NLogViewer、Null、OutputDebugString、PerfCounter、Trace、WebService(控制台、文件系统、数据库、电子邮件、网络Socket等)。
上述配置文件中的layout用来规定输出内容格式,语法“${属性}”,可以把上下文信息插入到日志中。通过全面的上下文信息构建一个结构化日志信息。
$ {cached} - 将缓存应用于另一个布局输出。
$ {db-null} - 为数据库渲染DbNull
$ {exception} - 通过调用Logger方法之一提供的异常信息
$ {level} - 日志级别(例如ERROR,DEBUG)或级别序数(数字)
$ {literal} - 字符串文字。(文本)-有用,以逃避括号
$ {logger} - 记录器名称。GetLogger,GetCurrentClassLogger等
$ {message} - (格式化的)日志消息。
$ {newline} - 换行文字。
$ {object-path} - 渲染对象的(嵌套)属性
$ {onexception} - 仅在为日志消息定义了异常时才输出内部布局。
$ {var} - 渲染变量
呼叫站点和堆栈跟踪-------------------------------------------------------------
$ {callsite} - 调用站点(类名,方法名和源信息)
$ {callsite-linenumber} - 呼叫站点源行号。
$ {stacktrace} - 渲染堆栈跟踪
条件------------------------------------------------------------------------
$ {when} - 仅在满足指定条件时输出内部布局。
$ {whenempty} - 当内部布局产生空结果时,输出替代布局。
上下文信息-------------------------------------------------------------------
$ {activityid} - 将System.Diagnostics跟踪关联ID记录到日志中。
$ {all-event-properties} - 记录所有事件上下文数据。
$ {event-context} - 记录事件属性数据-替换为$ {event-properties}
$ {event-properties} - 记录事件属性数据-重命名$ {event-context}
$ {gdc} - 全局诊断上下文项。包含每个应用程序实例值的字典结构。
$ {install-context} - 安装参数(传递给InstallNLogConfig)。
$ {mdc} - 映射诊断上下文-线程局部结构。
$ {mdlc} - 异步映射诊断上下文-线程局部结构。MDC的异步版本
$ {ndc} - 嵌套诊断上下文-线程局部结构。
$ {ndlc} - 异步嵌套诊断上下文-线程本地结构。
专柜-----------------------------------------------------------------------
$ {counter} - 一个计数器值(在每个布局渲染中增加)
$ {guid} - 全局唯一标识符(GUID)。
$ {sequenceid} - 日志序列号
日期和时间------------------------------------------------------------------
$ {date} - 当前日期和时间。
$ {longdate} - 日期和时间,采用可排序的长格式`yyyy-MM-dd HH:mm:ss.ffff`。
$ {qpc} - 高精度计时器,基于QueryPerformanceCounter返回的值。
$ {shortdate} - 短日期,格式为yyyy-MM-dd。
$ {ticks} - 当前日期和时间的“ Ticks”值。
$ {时间} - 在24小时,可排序的格式HH的时间:MM:ss.mmm。
编码和字符串转换--------------------------------------------------------------
$ {json-encode} - 使用JSON规则转义另一个布局的输出。
$ {left} - 文字的左半部分
$ {小写} - 将另一个布局输出的结果转换为小写。
$ {norawvalue} - 防止将另一个布局渲染器的输出视为原始值
$ {pad} - 将填充应用于另一个布局输出。
$ {replace} - 将另一个布局的输出中的字符串替换为另一个字符串。正则表达式可选
$ {replace-newlines} - 用另一个字符串替换换行符。
$ {right} - 文字的右侧
$ {rot13} - 使用ROT-13解码“加密”的文本。
$ {substring} - 文本的子字符串
$ {trim-whitespace} - 从另一个布局渲染器的结果修剪空白。
$ {uppercase} - 将另一个布局输出的结果转换为大写。
$ {url-encode} - 编码另一个布局输出的结果,以供URL使用。
$ {wrapline} - 以指定的行长包装另一个布局输出的结果。
$ {xml-encode} - 将另一个布局输出的结果转换为XML兼容的。
环境和配置文件----------------------------------------------------------------
$ {appsetting} -. config文件 NLog.Extended中的应用程序配置设置
$ {configsetting} - 来自appsettings.json或ASP.NET Core和.NET Core中其他配置的值 NLog.Extensions.Logging NLog.Extensions.Hosting NLog.Web.AspNetCore
$ {environment} - 环境变量。(例如PATH,OSVersion)
$ {environment-user} - 用户身份信息(用户名)。
$ {}注册表 - 从Windows注册表中的值。
文件和目录--------------------------------------------------------------------
$ {basedir} - 当前应用程序域的基本目录。
$ {currentdir} - 应用程序的当前工作目录。
$ {file-contents} - 呈现指定文件的内容。
$ {filesystem-normalize} - 通过将文件名替换为安全字符来过滤文件名中不允许的字符。
$ {} nlogdir - 其中NLog.dll所在的目录。
$ {specialfolder} - 系统专用文件夹路径(包括“我的文档”,“我的音乐”,“程序文件”,“桌面”等)。
$ {tempdir} - 临时目录。
身分识别----------------------------------------------------------------------
$ {identity} - 线程身份信息(名称和身份验证信息)。
$ {windows-identity} - 线程Windows身份信息(用户名)
$ {windows-identity} - 线程Windows身份信息(用户名) Nlog.WindowsIdentity
整合方式----------------------------------------------------------------------
$ {gelf} - 将日志转换为GELF格式 NLog.GelfLayout 外部
$ {log4jxmlevent} - 与log4j,Chainsaw和NLogViewer兼容的XML事件描述。
进程,线程和程序集--------------------------------------------------------------
$ {appdomain} - 当前的应用程序域。
$ {assembly-version} - 默认应用程序域中可执行文件的版本。
$ {gc} - 有关垃圾收集器的信息。
$ {hostname} - 运行该进程的计算机的主机名。
$ {local-ip} - 来自网络接口的本地IP地址。
$ {machinename} - 运行进程的计算机名。
$ {performancecounter} - 性能计数器。
$ {processid} - 当前进程的标识符。
$ {processinfo} - 有关正在运行的进程的信息。例如StartTime,PagedMemorySize
$ {processname} - 当前进程的名称。
$ {processtime} - 格式为HH:mm:ss.mmm的处理时间。
$ {threadid} - 当前线程的标识符。
$ {threadname} - 当前线程的名称。
Silverlight------------------------------------------------------------------------
$ {document-uri} - 承载当前Silverlight应用程序的HTML页面的URI。
$ {sl-appinfo} - 有关Silverlight应用程序的信息。
Web,ASP.NET和ASP.NET Core----------------------------------------------------------
$ {ASPNET-appbasepath} - ASP.NET应用程序的基本路径(内容根) NLog.Web NLog.Web.AspNetCore
$ {ASPNET应用} - ASP.NET应用程序变量。 网络日志
$ {ASPNET环境} - ASP.NET环境名称 NLog.Web.AspNetCore
$ {ASPNET项} - ASP.NET`HttpContext`项变量。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET-MVC-行动} - ASP.NET MVC动作名称 NLog.Web NLog.Web.AspNetCore
$ {ASPNET-MVC控制器} - ASP.NET MVC控制器名称 NLog.Web NLog.Web.AspNetCore
$ {ASPNET请求} - ASP.NET请求变量。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET-请求的contentType} - ASP.NET Content-Type头(实施例应用/ JSON) NLog.Web.AspNetCore
$ {ASPNET请求,饼干} - ASP.NET请求的cookie的内容。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET请求形式} - ASP.NET请求表的内容。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET请求报头} - ASP.NET部首键/值对。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET请求主机} - ASP.NET请求主机。 NLog.Web NLog.Web.AspNetCore
$ {aspnet-request-ip} - 客户端IP。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET请求-方法} - ASP.NET请求方法(GET,POST等)。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET请求,贴体} - ASP.NET贴体/净荷 NLog.Web NLog.Web.AspNetCore
$ {ASPNET-请求的查询字符串} - ASP.NET请求的查询字符串。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET,请求引荐} - ASP.NET请求引用。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET请求的URL} - ASP.NET请求URL。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET请求,用户代理} - ASP.NET请求用户代理。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET-响应的StatusCode} - ASP.NET响应状态代码的内容。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET会话} - ASP.NET Session变量。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET-的SessionID} - ASP.NET会话ID的变量。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET-traceidentifier} - ASP.NET跟踪标识 NLog.Web NLog.Web.AspNetCore
$ {ASPNET用户-的authType} - ASP.NET用户验证。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET用户身份} - ASP.NET用户变量。 NLog.Web NLog.Web.AspNetCore
$ {ASPNET用户-isauthenticated} - ASP.NET用户身份验证? NLog.Web NLog.Web.AspNetCore
$ {ASPNET-webrootpath} - ASP.NET Web根目录路径(wwwroot文件) NLog.Web NLog.Web.AspNetCore
$ {iis-site-name} - IIS网站的名称。 NLog.Web NLog.Web.AspNetCore
第三步、在program中注入nlog的服务。
using NLog; using NLog.Web; namespace tqf.nlog.Demo { public class Program { public static void Main(string[] args) { var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Debug("init main"); try { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // NLog: Setup NLog for Dependency injection builder.Logging.ClearProviders(); builder.Logging.AddNLogWeb("n.config");// 加载日志配置项文件,默认根目录下NLog.config builder.Host.UseNLog();// 使用NLog为Microsoft.Extensions.Logging的日志记录 var app = builder.Build(); // Configure the HTTP request pipeline. app.UseAuthorization(); app.MapControllers(); app.Run(); } catch (Exception ex) { logger.Error(ex, "Stopped program because of exception"); throw; } finally { NLog.LogManager.Shutdown(); } } } }
第四步、在业务中使用日志记录,通过ASP.NET Core框架的ILogger。
using Microsoft.AspNetCore.Mvc; namespace tqf.nlog.Demo.Controllers { [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; private readonly ILoggerFactory _loggerFactory; public WeatherForecastController(ILogger<WeatherForecastController> logger, ILoggerFactory loggerFactory) { _logger = logger; _loggerFactory = loggerFactory; } [HttpGet] public IEnumerable<WeatherForecast> Get() { _logger.LogInformation("使用NLog类库记录系统日志Information"); _logger.LogWarning("使用NLog类库记录系统日志Warning"); try { throw new Exception("抛出异常,记录异常日志"); } catch (Exception ex) { _logger.LogWarning(ex.Message); } _logger.LogError("系统错误信息!"); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } } }
第五步、完成上述操作,运行程序,打印日志。
3、实践Serilog
第一步、在tqf.serilog.Demo(.net 8.0)中引入包,主要包括Serilog的简单基础包,Serilog.AspNetCore准对ASP.NET Core支持的包,Serilog.Sinks.File对文件滚动记录的包。
<PackageReference Include="Serilog" Version="4.0.0" /> <PackageReference Include="Serilog.AspNetCore" Version="8.0.1" /> <PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" /> <PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
第二步、在tqf.serilog.Demo中新增json文件,主要配置使用方式,文件路基,文件名称等。
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "logs/log.txt",
"rollingInterval": "Day"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "MyApp"
}
}
}
第三步、在program中注入serilog的服务。
using Serilog; using Serilog.Events; namespace tqf.Serilog.Demo { public class Program { public static void Main(string[] args) { // 本处使用自定义配置项 Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .Enrich.FromLogContext() .WriteTo.Console() .WriteTo.Logger(lc => lc.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Information) .WriteTo.File(Path.Combine(Environment.CurrentDirectory, "logs", "Information", "log.txt"), rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1), fileSizeLimitBytes: 10 * 1024 * 1024)) .WriteTo.Logger(lc => lc.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Warning) .WriteTo.File(Path.Combine(Environment.CurrentDirectory, "logs", "Warning", "log.txt"), rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1), fileSizeLimitBytes: 10 * 1024 * 1024)) .WriteTo.Logger(lc => lc.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Error) .WriteTo.File(Path.Combine(Environment.CurrentDirectory, "logs", "Error", "log.txt"), rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1), fileSizeLimitBytes: 10 * 1024 * 1024)) .WriteTo.Logger(lc => lc.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Debug) .WriteTo.File(Path.Combine(Environment.CurrentDirectory, "logs", "Debug", "log.txt"), rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1), fileSizeLimitBytes: 10 * 1024 * 1024)) /* .WriteTo.File( System.IO.Path.Combine(Environment.CurrentDirectory, "LogFiles", "Application", "log.txt"), rollingInterval: RollingInterval.Day, fileSizeLimitBytes: 10 * 1024 * 1024, retainedFileCountLimit: 2, rollOnFileSizeLimit: true, shared: true, flushToDiskInterval: TimeSpan.FromSeconds(1)) */ .CreateLogger(); var builder = WebApplication.CreateBuilder(args); // Add services to the container. /* // 使用json配置文件 var logger = new LoggerConfiguration() .ReadFrom.Configuration(builder.Configuration.AddJsonFile("serilog.json").Build()) .CreateLogger(); builder.Logging.AddSerilog(logger); */ builder.Services.AddSerilog(); builder.Services.AddControllers(); var app = builder.Build(); // Configure the HTTP request pipeline. app.UseAuthorization(); app.MapControllers(); app.Run(); } } }
第四步、在业务中使用日志记录,通过ASP.NET Core框架的ILogger。
using Microsoft.AspNetCore.Mvc; namespace tqf.Serilog.Demo.Controllers { [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet] public IEnumerable<WeatherForecast> Get() { _logger.LogInformation("使用Serilog类库记录系统日志Information"); _logger.LogWarning("使用Serilog类库记录系统日志Warning"); try { throw new Exception("抛出异常,记录异常日志"); } catch (Exception ex) { _logger.LogWarning(ex.Message); } _logger.LogError("系统错误信息!"); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } } }
第五步、完成上述操作,运行程序,打印日志。
补充:Serilog组件在日志管理提供强大的类库支持,比如在项目列举serilog、serilog-enrichers-thread、serilog-enrichers-process、serilog-expressions、serilog-sinks-email、serilog-sinks-eventlog、serilog-sinks-trace、serilog-enrichers-environment、serilog-formatting-compact、serilog-sinks-console、serilog-sinks-async、serilog-sinks-opentelemetry、serilog-settings-configuration、serilog-sinks-periodicbatching、serilog-extensions-logging、serilog-aspnetcore、serilog-extensions-hosting、serilog-sinks-file、serilog-formatting-compact-reader、serilog-sinks-browserconsole、serilog-sinks-xamarin、serilog-extensions-logging-file、serilog-sinks-rollingfile、serilog-sinks-textwriter、serilog-settings-appsettings、serilog-sinks-debug、serilog-sinks-map、serilog.github.io、serilog-sinks-observable,通过上述项目及名称可以看出Serilog各种日志使用方式,场景,跨平台。
四、总结
通过上述介绍、对使用的三个流行的日志记录库总结对比一下?
1、log4net:优点是灵活性,适用于多种情况,提供全方面的配置项,控制日志的记录管理;成熟稳定性,该日志类库历史悠久,存在时间长,广泛接受和庞大的社区。缺点是:缺乏结构化日志记录,其提供日志信息没有相对结构化,信息难于查找;配置复杂,必须深入了解各个节点项,多个结合来配置达到场景要求。
2、NLog:优点是:灵活性,提供可配置性,多个输出目标,与log4net一样灵活;结构化日志记录,与log4net相比新增结构化日志,但是与Serilog不会过于复杂。缺点是:在成熟度上,没有log4net的经过长时间验证;在结构化日志上没有Serilog发展和复杂,提供相对简单,如果对结构化日志深入探索推进Serilog。
3、Serilog:优点是:结构化日志记录,赋予日志超能力,在结构化和组织化下对记录日志进行查询不在困难;丰富的输出支持,将日志记录到控制台或文件是“古老”的方式,随着系统架构的发展,日志管理也要适应架构,包括分布式日志、基于云的日志服务等;配置选项,提供配置文件或者编程的方式进行配置。缺点是:.NET 4.5+版本要求;复杂性对仅仅是简单的日志记录,可以考虑使用log4net。
通过指标上结构化日志记录、高可配置性、高性能、社区支持上对比除了log4net的差点,其它NLog、Serilog都是不错。
通过上述对比,Demo的实践,开发者如何选择一款日志库?
主要从如下几方面综合考虑:1、日志内容是纯文本、属性丰富的事件,如果对结构化要求比较高,首先Serilog提供全方位的结构化功能,仅仅是文件日志log4net是不错的选择,想中和两则考虑使用NLog日志,平衡的替代方案。2、库的提供,考虑库的生态,社区支持,输出目标的支持。3、集成,考虑库集成系统的成本。4、性能和易用性:如果你的应用程序处理大量日志,则有必要确保你选择的库不仅速度快,而且易于查询和过滤。
总之,依据不同的场景选择不同的日志库,不过根据当前业务复杂性,微服务,CICD,容器计算,云原生等技术的发展,对于基础设施日志管理也必须迭代更新,Serilog在目前来看不是比较好的选择。
参考:https://www.bytehide.com/blog/serilog-log4net-nlog-comparison,https://www.cnblogs.com/tonwin/p/18117888
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求