NLog多场景下控制台不同JSON格式日志输出方案咨询
最优方案:利用NLog的条件规则与结构化日志能力,避免手动拼接JSON
针对你在WebAPI2 + NLog 4.0.3场景下需要输出10种不同JSON格式控制台日志的需求,我推荐你优化方案1的思路,通过NLog内置的条件路由和MDLC(映射诊断逻辑上下文)来实现——完全不需要手动拼接JSON(方案2的手动拼接容易出现转义错误、难以维护,还浪费了NLog的结构化日志能力)。
一、核心思路
- 为每种日志场景配置独立的
ConsoleTarget,每个Target对应专属的JsonLayout结构 - 通过NLog的规则(Rules)配置条件过滤,根据日志上下文(比如MDLC标记、是否存在异常、日志级别)自动路由到对应Target
- 代码中通过MDLC传递场景专属的字段值,无需手动拼接JSON字符串
二、具体配置(web.config)
首先在配置文件中定义所有场景的Target和对应的条件规则:
<configSections> <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" /> </configSections> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <targets> <!-- 场景1:异常日志Target --> <target xsi:type="Console" name="console_exception" > <layout xsi:type="JsonLayout"> <attribute name="id" layout="${mdlc:item=id}" /> <attribute name="uuid" layout="${mdlc:item=uuid}" /> <attribute name="key" layout="${mdlc:item=key}" /> <attribute name="message" layout="${message}" /> <attribute name="stack_trace" layout="${exception:format=ToString}" /> </layout> </target> <!-- 场景2:数据库耗时日志Target --> <target xsi:type="Console" name="console_databaseTimeTaken" > <layout xsi:type="JsonLayout"> <attribute name="id" layout="${mdlc:item=id}" /> <attribute name="message" layout="Time taken to retrieve data from database" /> <attribute name="Time" layout="${message}" /> </layout> </target> <!-- 其他8种场景的Target,按照同样的方式配置专属JsonLayout --> </targets> <rules> <!-- 异常日志规则:当日志携带异常时,路由到console_exception --> <logger name="*" minLevel="Error" writeTo="console_exception" when="${exception:innerFormat=ToString} != ''" /> <!-- 数据库耗时日志规则:当MDLC中存在LogType=DatabaseTime标记时,路由到console_databaseTimeTaken --> <logger name="*" minLevel="Info" writeTo="console_databaseTimeTaken" when="${mdlc:item=LogType} == 'DatabaseTime'" /> <!-- 其他场景的规则:根据各自的判断条件(比如MDLC标记、日志级别、特定关键字)配置对应的when表达式 --> </rules> </nlog>
三、代码中如何使用
1. 记录异常日志
using NLog; using NLog.MappedDiagnosticsLogicalContext; // 初始化Logger private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); try { // 你的业务逻辑代码 } catch (Exception ex) { // 使用MDLC传递场景专属字段,using块确保上下文自动清理 using (MappedDiagnosticsLogicalContext.SetScoped("id", "5656")) using (MappedDiagnosticsLogicalContext.SetScoped("uuid", "xdd895-65454")) using (MappedDiagnosticsLogicalContext.SetScoped("key", "somekeyname")) { _logger.Error(ex, "Exception occurred during operation"); } }
2. 记录数据库耗时日志
var timeTakenInMs = 1100; using (MappedDiagnosticsLogicalContext.SetScoped("id", "8898998")) using (MappedDiagnosticsLogicalContext.SetScoped("LogType", "DatabaseTime")) { // 这里的日志消息会被填充到JsonLayout的Time字段中 _logger.Info($"{timeTakenInMs} (in milli seconds)"); }
四、为什么这个方案是最优的
- 避免手动拼接错误:NLog的
JsonLayout会自动处理JSON转义,再也不用担心日志内容里的双引号破坏JSON结构 - 易维护扩展:新增场景只需要添加新的Target和规则,无需修改业务代码;后续如果要把日志输出到文件、ELK等平台,只需要复用现有Layout配置
- 上下文管理更规范:MDLC的
SetScoped方法会自动清理上下文,避免内存泄漏或上下文污染 - 完全兼容你的技术栈:MDLC和条件规则在NLog 4.0.3中已经完全支持,不需要升级依赖
五、对原有方案的补充说明
- 你之前的方案1没找到正确的条件路由方式,通过
when表达式和MDLC标记就可以完美解决日志路由问题 - 方案2的手动拼接JSON是不推荐的,不仅容易出错,还丢失了NLog结构化日志的所有优势
内容的提问来源于stack exchange,提问作者Gowtham.K.Reddy
相关产品推荐
相关产品推荐

