刚接触NLog时,我也被它的配置文件吓到过。但实际用起来你会发现,这可能是.NET生态中最容易上手的日志组件之一。先来看看最简配置——新建一个.NET Core控制台项目,通过NuGet安装NLog和NLog.Config两个包后,项目根目录会自动生成NLog.config文件。这个XML文件就是整个日志系统的控制中心。
我建议初学者先关注三个核心配置块:
xml复制<targets>
<target name="logfile" xsi:type="File"
fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate}|${level}|${message}" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile" />
</rules>
这段配置做了三件事:定义了一个文件类型的日志目标(target),设置了日志格式(layout),并通过规则(rule)将所有Debug级别以上的日志写入文件。实测发现,很多新手卡在第一步就是因为没注意basedir这个关键变量——它表示应用程序的工作目录,在IIS或Docker中运行时,这个路径可能和你想象的不一样。
当项目进入生产环境后,简单的单文件日志就不够用了。这是我优化过的多文件配置方案:
xml复制<targets>
<target name="dailyFile" xsi:type="File"
fileName="${basedir}/logs/${shortdate}.log"
archiveFileName="${basedir}/logs/archives/{#}.log"
archiveEvery="Day"
archiveNumbering="Rolling"
maxArchiveFiles="30"
layout="${longdate}|${level}|${logger}|${message}" />
<target name="errorFile" xsi:type="File"
fileName="${basedir}/logs/errors.log"
layout="${longdate}|${level}|${stacktrace}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="dailyFile" />
<logger name="*" minlevel="Error" writeTo="errorFile" />
</rules>
这个配置实现了:按天分割日志文件、自动归档旧日志(保留30天)、错误日志单独存储。特别提醒archiveEvery参数,除了Day还支持Hour/Minute,我在处理高并发系统时会设置为Hour。
现代系统往往需要同时输出到多个目的地。这是我常用的组合方案:
xml复制<targets>
<target name="file" xsi:type="File" fileName="${basedir}/logs/app.log" />
<target name="console" xsi:type="Console" />
<target name="database" xsi:type="Database"
connectionString="..."
commandText="INSERT INTO Logs(...) VALUES(...)" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="file,console" />
<logger name="Critical.*" minlevel="Error" writeTo="database" />
</rules>
开发阶段我会同时输出到控制台和文件,生产环境则增加数据库存储。注意数据库写入最好配合异步包装器(AsyncWrapper),否则可能影响系统性能。
在IIS中运行时,90%的日志丢失问题都是权限导致的。正确的做法是:
xml复制fileName="C:\AppLogs\${shortdate}.log"
throwExceptions="true"临时开启异常抛出,便于排查问题容器化部署时要注意三点:
xml复制fileName="${environment:LOG_PATH=/var/log}/app.log"
networkFile替代普通file类型,解决容器间共享日志的问题作为服务运行时,推荐采用这种配置:
xml复制<target name="serviceLog" xsi:type="File"
fileName="${specialfolder:folder=CommonApplicationData}/MyApp/logs/app.log"
keepFileOpen="true"
concurrentWrites="false" />
keepFileOpen能显著提升性能,但要注意它可能导致日志文件被长期锁定。我在金融级系统中会额外配置networkFile作为灾备方案。
虽然NLog默认支持异步,但配置不当反而会丢日志。这是我的最佳实践:
xml复制<targets async="true">
<target name="asyncFile" xsi:type="AsyncWrapper"
queueLimit="10000"
overflowAction="Block">
<target xsi:type="File" fileName="${basedir}/logs/async.log" />
</target>
</targets>
关键参数overflowAction建议设为Block(阻塞)而非Discard(丢弃),特别是在交易类系统中。测试表明,当队列满时Block模式会降低吞吐量但能保证日志完整性。
通过API动态调整日志级别可以快速定位生产问题:
csharp复制// 获取指定logger的配置
var rule = LogManager.Configuration.FindRuleByName("Important.Logger");
// 动态修改级别
rule.SetLoggingLevels(LogLevel.Debug, LogLevel.Fatal);
// 应用更改
LogManager.ReconfigExistingLoggers();
我在运维平台中集成了这个功能,配合条件日志规则,可以精准捕获特定用户的请求日志。
现代日志系统越来越强调结构化。NLog通过LayoutRenderer支持JSON输出:
xml复制<target name="jsonFile" xsi:type="File"
fileName="${basedir}/logs/structured.json">
<layout xsi:type="JsonLayout">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}" />
<attribute name="message" layout="${message}" />
<attribute name="exception" layout="${exception:format=ToString}" />
</layout>
</target>
这样的日志可以直接导入ELK等分析系统。建议关键业务系统至少对错误日志采用JSON格式。