Log4Net 老而稳、XML配置、弱结构化NLog 全能中庸、配置灵活、强扩展Serialog 现代原生结构化、代码优先、适合云原生/ELK。一、基本信息1、Log4NetJava log4j的.Net移植经典老牌、稳定、生态大.Net Framework时代事实标准。2、NLog.Net 原生开发功能全面、配置灵活、高性能兼顾传统与现代企业级常用。3、Serilog现代结构化日志代表原生JSON代码优先配置与 .NET Core/DI/ELK 生态无缝集成。二、核心差异1、结构化日志1Log4Net原生不支持只能用字符串拼接需手动扩展才能勉强结构化日志是纯文本。2NLog支持结构化需配置开启输出可含字段比Log4Net强但不如Serilog。3Serilog原生强结构化模板参数自动提取为独立字段直接输出Json机器可直接查询。2、配置方式1Log4NetXML为主配置集中但繁琐、易储出错、无编译检查支持运行时重载。2NLogXML/JSON/代码 三选一灵活规则引擎强支持条件路由、环境变量、动态重载。3Serilog代码优先简洁强类型、编译检查也支持JSON配置但不常用。3、性能高并发场景1Log4Net同步写入为主全局锁高并发易瓶颈异步需第三方包装性能一般。2NLog内置异步队列、细粒度锁吞吐高正确配置下性能接近Serilog。3Serilog无共享状态、异步批量写入结构化场景性能最优但JSON序列化有开销。4、输出目标1Log4NetAppender日志追加器多但偏传统文件、控制台、数据库现代生态支持弱。2NLog200 Targets目标几乎覆盖所有文件、数据库、队列、云等企业级最全面。3SerilogSinks下沉点丰富主打现代栈Elasticsearch、Seq、File云原生友好。5、.Net生态集成1Log4Net.Net Framework 友好.Net Core 适配。2NLog全平台兼容DI依赖注入支持好容器化友好。3Serilog.Net Core/DI 原生集成云原生首选。三、优缺点速览1、Log4Net优点极度稳定、成熟、社区大、问题易查XML集中配置传统项目维护友好。缺点无原生结构化同步性能差扩展能力弱。适用存量工控项目维持不动工厂现场纯本地文本日志、不需要日志收集、不需要检索分析。2、NLog优点全能中庸、配置灵活、高性能、扩展极强支持动态重载、多目标、结构化全平台兼容。缺点结构化不如SerilogXML配置复杂。适用新建上位机项目政企内网企业项目既要本地日志稳定又要偶尔简单结构化、多环境日志隔离。3、Serilog优点原生结构化、代码配置简洁、现代生态完美集成、云原生首选。缺点JSON序列化有开销传统XMl配置支持弱不如NLog开箱即用。适用企业级、微服务、云服务、需要日志集中收集分析项目需要日志检索、链路追踪、异常数据分析的项目。四、WPF项目中使用配置1、Log4Net1NuGet 安装包log4net2新增配置文件log4net.configlog4net !--根配置-- root !--日志级别:可选值: ERROR WARN INFO DEBUG -- level valueERROR/ level valueWARN/ level valueINFO/ level valueDEBUG/ appender-ref refErrorLog / appender-ref refWarnLog / appender-ref refInfoLog / appender-ref refDebugLog / /root !-- 错误 Error.log-- appender nameErrorLog typelog4net.Appender.RollingFileAppender !--目录路径可以是相对路径:log 或绝对路径: C:\WindowsFormsPro_log -- param nameFile valueLogs/ !--文件名按日期生成文件夹 /yyyy-MM-dd/quot;Error.logquot; -- param nameDatePattern value/yyyy-MM-dd/quot;Error.logquot;/ !--追加到文件-- appendToFile valuetrue/ !--防止多线程时不能写Log,官方说线程非安全-- lockingModel typelog4net.Appender.FileAppenderMinimalLock / !--创建日志文件的方式可选值Date[日期],文件大小[Size],混合[Composite]-- rollingStyle valueComposite/ !--写到一个文件-- staticLogFileName valuefalse/ !--单个文件大小。单位:KB|MB|GB-- maximumFileSize value10MB/ !--最多保留的文件数设为-1则不限-- maxSizeRollBackups value3/ !--日志格式-- layout typelog4net.Layout.PatternLayout conversionPattern value%message/ /layout filter typelog4net.Filter.LevelRangeFilter param nameLevelMin valueERROR / param nameLevelMax valueERROR / /filter /appender !-- 警告 Warn.log-- appender nameWarnLog typelog4net.Appender.RollingFileAppender !--目录路径可以是相对路径或绝对路径-- param nameFile valueLogs/ !--文件名按日期生成文件夹-- param nameDatePattern value/yyyy-MM-dd/quot;Warn.logquot;/ !--追加到文件-- appendToFile valuetrue/ !--防止多线程时不能写Log,官方说线程非安全-- lockingModel typelog4net.Appender.FileAppenderMinimalLock / !--创建日志文件的方式可选值Date[日期],文件大小[Size],混合[Composite]-- rollingStyle valueComposite/ !--写到一个文件-- staticLogFileName valuefalse/ !--单个文件大小。单位:KB|MB|GB-- maximumFileSize value10MB/ !--最多保留的文件数设为-1则不限-- maxSizeRollBackups value3/ !--日志格式-- layout typelog4net.Layout.PatternLayout conversionPattern value%message/ /layout filter typelog4net.Filter.LevelRangeFilter param nameLevelMin valueWARN / param nameLevelMax valueWARN / /filter /appender !-- 信息 Info.log-- appender nameInfoLog typelog4net.Appender.RollingFileAppender !--目录路径可以是相对路径或绝对路径-- param nameFile valueLogs/ !--文件名按日期生成文件夹-- param nameDatePattern value/yyyy-MM-dd/quot;Info.logquot;/ !--追加到文件-- appendToFile valuetrue/ !--防止多线程时不能写Log,官方说线程非安全-- lockingModel typelog4net.Appender.FileAppenderMinimalLock / !--创建日志文件的方式可选值Date[日期],文件大小[Size],混合[Composite]-- rollingStyle valueComposite/ !--写到一个文件-- staticLogFileName valuefalse/ !--单个文件大小。单位:KB|MB|GB-- maximumFileSize value10MB/ !--最多保留的文件数设为-1则不限-- maxSizeRollBackups value3/ !--日志格式-- layout typelog4net.Layout.PatternLayout conversionPattern value%message/ /layout filter typelog4net.Filter.LevelRangeFilter param nameLevelMin valueINFO / param nameLevelMax valueINFO / /filter /appender !-- 调试 Debug.log-- appender nameDebugLog typelog4net.Appender.RollingFileAppender !--目录路径可以是相对路径或绝对路径-- param nameFile valueLogs/ !--文件名按日期生成文件夹-- param nameDatePattern value/yyyy-MM-dd/quot;Debug.logquot;/ !--追加到文件-- appendToFile valuetrue/ !--防止多线程时不能写Log,官方说线程非安全-- lockingModel typelog4net.Appender.FileAppenderMinimalLock / !--创建日志文件的方式可选值Date[日期],文件大小[Size],混合[Composite]-- rollingStyle valueComposite/ !--写到一个文件-- staticLogFileName valuefalse/ !--单个文件大小。单位:KB|MB|GB-- maximumFileSize value10MB/ !--最多保留的文件数设为-1则不限-- maxSizeRollBackups value3/ !--日志格式-- layout typelog4net.Layout.PatternLayout conversionPattern value%message/ /layout filter typelog4net.Filter.LevelRangeFilter param nameLevelMin valueDEBUG / param nameLevelMax valueDEBUG / /filter /appender /log4net3全局加载配置文件// 全局加载日志配置文件 Log4 XmlConfigurator.Configure(new FileInfo(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, XmlConfig\\log4net.config)));4使用public class MainWindowViewModel : BindableBase { private readonly ILogger _logger; // 依赖注入 public MainWindowViewModel(ILogger logger) { _logger logger; // 日志记录 _logger.Debug(Debug日志信息); _logger.Warn(Warn日志信息); _logger.Info(Info日志信息); _logger.Error(Error日志信息); } }2、NLog1NuGet 安装包NLog2新增配置文件NLog.confignlog xmlnshttp://www.nlog-project.org/schemas/NLog.xsd xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://www.nlog-project.org/schemas/NLog.xsd NLog.xsd autoReloadtrue throwExceptionsfalse internalLogLevelOff internalLogFilenlog-internal.log !-- optional, add some variables https://github.com/nlog/NLog/wiki/Configuration-file#variables -- variable namemyvar valuemyvalue/ !-- See https://github.com/nlog/nlog/wiki/Configuration-file for information on customizing logging rules and outputs. -- targets asynctrue !-- add your targets here See https://github.com/nlog/NLog/wiki/Targets for possible targets. See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers. -- !--写入数据库-- !--target nameAllDatabase xsi:typeDatabase dbProviderSystem.Data.SqlClient.SqlConnection, System.Data.SqlClient connectionStringData SourceDESKTOP-T2D6ILD;Initial CatalogLogManager;Persist Security InfoTrue;User IDsa;Passwordsa123 commandTextinsert into dbo.NLog (Application, Logged, Level, Message,Logger, CallSite, Exception) values (Application, Logged, Level, Message,Logger, Callsite, Exception); parameter nameapplication layoutAspNetCoreNlog / parameter namelogged layout${date} / parameter namelevel layout${level} / parameter namemessage layout${message} / parameter namelogger layout${logger} / parameter namecallSite layout${callsite:filenametrue} / parameter nameexception layout${exception:tostring} / /target-- !--写入文件-- !--target xsi:typeFile nameallfile fileNameNLog\nlog-all-${shortdate}.log layout${longdate}|${logger}|${uppercase:${level}}|${message} ${exception} /-- !--同样是将文件写入日志中写入的内容有所差别差别在layout属性中体现。 写入日志的数量有差别差别在路由逻辑中体现-- !--target xsi:typeFile nameownFile-web fileNameNLog\nlog-my-${shortdate}.log layout${longdate}|${logger}|${uppercase:${level}}|${message} ${exception} /-- !--滚动日志文件上限数滚动日志文件数达到上限新的文件内容会覆盖旧文件内容 -- !--archiveAboveSize每个日志文件大小的最大值单位字节主日志文件超过大小超过该值时会将文件内容写入滚动日志并清空主日志文件内容-- !--${basedir}表示当前应用程序域所在的根目录-- !--keepFileOpenfalse 的作用 文件处理方式 false每次写入日志时打开文件写入完成后立即关闭 true在应用程序启动时就打开文件并保持打开状态直到程序结束-- !--target nameallfile xsi:typeFile fileName${basedir}/adminlogs/all.log archiveFileName${basedir}/adminlogs/all.{###}.txt archiveAboveSize20000000 maxArchiveFiles30 keepFileOpenfalse layout${longdate} | ${event-properties:itemEventId_Id} | ${uppercase:${level}} | ${logger} | ${aspnet-request-iP} | ${event-properties:itemuser} | ${aspnet-request-url} | ${message} | ${event-properties:itemrequestParam} | ${event-properties:itemjsonResult} | ${onexception:${exception:formattostring}/-- !-- 补充按级别分文件输出 -- !--${newline} 换行-- !--layout${longdate}${newline}级别: ${level:uppercasetrue}${newline}类名: ${logger}${newline}方法: ${callsite:classNametrue:methodNametrue}${newline}消息: ${message}${newline}异常: ${exception:formattostring}${newline}${newline}/-- target namedebugFile xsi:typeFile fileName${basedir}/logs/debug-${shortdate}.log archiveAboveSize20000000 maxArchiveFiles4 keepFileOpenfalse layout${longdate} | ${level} | ${logger} | ${callsite:classNametrue:methodNametrue} | ${message} ${exception}/ target nameinfoFile xsi:typeFile fileName${basedir}/logs/info-${shortdate}.log archiveAboveSize20000000 maxArchiveFiles4 keepFileOpenfalse layout${longdate} | ${level} | ${logger} | ${callsite:classNametrue:methodNametrue} | ${message} ${exception}/ target namewarnFile xsi:typeFile fileName${basedir}/logs/warn-${shortdate}.log archiveAboveSize20000000 maxArchiveFiles4 keepFileOpenfalse layout${longdate} | ${level} | ${logger} | ${callsite:classNametrue:methodNametrue} | ${message} ${exception}/ target nameerrorFile xsi:typeFile fileName${basedir}/logs/error-${shortdate}.log archiveAboveSize20000000 maxArchiveFiles4 keepFileOpenfalse layout${longdate} | ${level} | ${logger} | ${callsite:classNametrue:methodNametrue} | ${message} ${exception}/ !--写入控制台-- target nameconsole xsi:typeColoredConsole layout${date:formatMM-dd HH\:mm\:ss} | ${uppercase:${level}} | ${logger} | ${aspnet-request-iP} | ${aspnet-request-url} | ${message}/ !--写入黑洞-- target xsi:typeNull nameblackhole / !-- Write events to a file with the date in the filename. target xsi:typeFile namef fileName${basedir}/logs/${shortdate}.log layout${longdate} ${uppercase:${level}} ${message} / -- /targets rules logger name* minlevelDebug maxlevelDebug writeTodebugFile/ logger name* minlevelInfo maxlevelInfo writeToinfoFile/ logger name* minlevelWarn maxlevelWarn writeTowarnFile/ logger name* minlevelError writeToerrorFile/ !--logger name* minlevelTrace writeToAllDatabase /-- !-- add your logging rules here -- !--路由顺序会对日志打印产生影响。路由匹配逻辑为顺序匹配。-- !--All logs, including from Microsoft-- !--logger name* minlevelTrace writeToallfile /-- !--Skip Microsoft logs and so log only own logs-- !--以Microsoft打头的日志将进入此路由由于此路由没有writeTo属性所有会被忽略-- !--且此路由设置了final所以当此路由被匹配到时。不会再匹配此路由下面的路由。未匹配到此路由时才会继续匹配下一个路由-- !--logger nameMicrosoft.* minlevelTrace finaltrue /-- !--上方已经过滤了所有Microsoft.*的日志所以此处的日志只会打印除Microsoft.*外的日志-- !--logger name* minlevelTrace writeToownFile-web /-- !-- Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to f logger name* minlevelDebug writeTof / -- /rules /nlog3全局加载配置文件// 配置NLog LogManager.Setup().LoadConfigurationFromFile(XmlConfig\\NLog.config);4使用public class MainWindowViewModel : BindableBase { private readonly ILogger _logger; // 依赖注入 public MainWindowViewModel(ILogger logger) { _logger logger; // 日志记录 _logger.Debug(Debug日志信息); _logger.Warn(Warn日志信息); _logger.Info(Info日志信息); _logger.Error(Error日志信息); } }3、Serilog1NuGet 安装包SerilogSerilog.Sinks.FileSerilog.Sinks.Console。2代码配置Serilog// 1. 配置 Serilog全局静态 private void ConfigureSerilog() { Log.Logger new LoggerConfiguration() // 最小日志级别 Debug Information Warning Error Fatal .MinimumLevel.Information() // 输出到控制台 .WriteTo.Console() // 输出到文件按天切割保留30天 .WriteTo.File( path: Logs\\.log, // 日志目录Logs日志文件夹 rollingInterval: RollingInterval.Day, // 按天切割 retainedFileCountLimit: 30, // 保留30天 fileSizeLimitBytes: 1024 * 1024 * 10 // 单个文件最大10M ) .CreateLogger(); } protected override void OnStartup(StartupEventArgs e) { ConfigureSerilog(); base.OnStartup(e); }3全局注册protected override void RegisterTypes(IContainerRegistry containerRegistry) { // 全局注册 ILogger 单例 var logger Log.Logger; containerRegistry.RegisterInstanceILogger(logger); }4使用public class MainWindowViewModel : BindableBase { private readonly ILogger _logger; public MainWindowViewModel(ILogger logger) { _logger logger; _logger.Information(ViewModel 初始化); _logger.Warning(ViewModel 初始化); _logger.Error(ViewModel 初始化); _logger.Debug(ViewModel 初始化); } }