根本原因是logmanager.configuration在首次调用getcurrentclasslogger()时为null,导致日志静默丢弃;需确保nlog.config设为“始终复制”、生成操作为“内容”,并在任何logger调用前完成配置赋值并调用reconfigexistingloggers()。

日志写不出来、路径不对、环境切不走、归档失效——这些问题基本都出在配置方式和加载时机上,而不是NLog本身有问题。
为什么NLog.config“配对了”却没日志?
根本原因就一个:LogManager.Configuration 在首次调用 LogManager.GetCurrentClassLogger() 时为 null,之后所有日志都会静默丢弃,不报错也不提示。
- 检查
bin/Debug或bin/Release目录下是否存在NLog.config:没有就说明“复制到输出目录”没设对 - 右键
NLog.config→ 属性 → “复制到输出目录”必须是 始终复制(不是“如果较新则复制”,缓存可能跳过) - “生成操作”必须是
内容,“自定义工具”必须为空(否则 MSBuild 可能当代码文件处理) - ASP.NET Core 项目中,
Program.cs里若提前调用了LogManager.GetCurrentClassLogger()(比如在WebHost.CreateDefaultBuilder()之前),也会触发空配置加载
FileTarget 归档参数怎么配才不翻车?
archiveEvery、archiveFileName、archiveNumbering 必须协同生效,漏一个就等于没设归档。
-
archiveEvery="Day"单独存在无效,必须配合archiveFileName使用 -
archiveAboveSize="102400"触发大小归档时,archiveNumbering必须设为"Rolling"或"Sequence",否则不会生成新归档文件 -
maxArchiveFiles="30"控制的是归档目录下的文件总数,不是保留天数;如果每天归档 2 个文件,那最多只存 15 天 -
createDirs="true"必须显式开启,否则${basedir}/logs/不存在时会直接失败(不创建目录也不报错)
用 C# 代码动态配置 NLog 的关键时机
不能靠“先 new Logger 再配 config”,必须在任何 LogManager.*Logger() 调用前完成赋值。
- 控制台/Windows Forms:在
Main()最开头执行LogManager.Configuration = config; - ASP.NET Core:在
Program.cs中,WebApplication.CreateBuilder()之前初始化并赋值 - 不要在静态字段或构造函数里调用
GetCurrentClassLogger()—— 此时Configuration还是null - 多环境切换示例:
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production") { config.AddRule(...new FileTarget(...)); }
结构化日志为什么还是 plain text?
NLog 默认不解析占位符(如 "User {Id} logged in"),它把整个字符串当普通文本渲染,除非你显式启用结构化支持。
- 需要安装
NLog.StructuredLogging.Json或手动解析:用LogEventInfo构造 +Logger.Log()方法传参 - 更实用的做法:用
LayoutRenderer配合 JSON 格式,例如layout="${longdate} ${json-encode:inner=${message}}" - 若对接 Seq / Elasticsearch,建议直接用
JsonLayout并设置includeAllProperties="true" - 注意:
${event-properties:item=xxx}只对通过LogEventInfo.Properties显式附加的属性有效,不是自动从插值里提取
最常被忽略的点:LogManager.Configuration 是静态只读引用,替换后旧 logger 实例不会自动更新目标;一旦设过,后续修改必须重新赋值并调用 LogManager.ReconfigExistingLoggers() 才生效。










