封装Logger.Error方法后,NLog为何无法动态替换占位符?
问题场景
在遗留ASP.NET项目中使用NLog.Web 5.3.8版本,未采用依赖注入。封装了如下日志方法:
private static readonly ILogger Logger = LogManager.GetLogger("WebService"); public static void LogError(string message, params string[] args) { Logger.Error(message, args); }
调用代码:
TestClass.LogError("{A1}, {A2}", "B1", "B1");
实际日志输出:
2024-04-11 18:12:50.7385|ERROR|TestClass|{A1}, {A2}
预期输出:
2024-04-11 18:12:50.7385|ERROR|TestClass|B1, B1
已尝试string.Format()、数字索引占位符({1}、{2}),怀疑问题出在params string[]参数上。附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"> <extensions> <add assembly="NLog.Web"/> <add assembly="NewRelic.LogEnrichers.NLog" /> </extensions> <targets> <target name="NewRelicLogFile" xsi:type="File" fileName="{REPLACED}" archiveFileName="{REPLACED}" archiveNumbering="Date" archiveDateFormat="yyyyMMdd" archiveEvery="Day" maxArchiveFiles="30"> <layout xsi:type="newrelic-jsonlayout"> <attribute name="time" layout="${longdate}" /> <attribute name="level" layout="${uppercase:${level}}" /> <attribute name="traceId" layout="${activityid:whenEmpty=${mdlc:item=RequestId:whenEmpty=${aspnet-TraceIdentifier}}}" /> <attribute name="threadId" layout="${threadid}" /> <attribute name="logger" layout="${logger}" /> <attribute name="errorCode" layout="${event-properties:item=ErrorCode}" /> <attribute name="errorClass" layout="${exception:format=Type}" /> <attribute name="errorMessage" layout="${exception:format=Message}" /> <attribute name="errorStack" layout="${exception:format=StackTrace}" /> <attribute name="message" layout="${event-properties:item=Message:whenEmpty=${message}}" /> <attribute name="applicationName" layout="${appdomain:format={1\}}" /> <attribute name="area" layout="${event-properties:item=Area:whenEmpty=${logger}}" /> </layout> </target> <target name="NewRelicLogFile" xsi:type="File" fileName="{REPLACED}" archiveNumbering="Date" archiveDateFormat="yyyyMMdd" archiveEvery="Day" maxArchiveFiles="30"> <layout xsi:type="newrelic-jsonlayout"> <attribute name="time" layout="${longdate}" /> <attribute name="level" layout="${uppercase:${level}}" /> <attribute name="traceId" layout="${activityid:whenEmpty=${mdlc:item=RequestId:whenEmpty=${aspnet-TraceIdentifier}}}" /> <attribute name="threadId" layout="${threadid}" /> <attribute name="logger" layout="${logger}" /> <attribute name="errorCode" layout="${event-properties:item=ErrorCode}" /> <attribute name="errorClass" layout="${exception:format=Type}" /> <attribute name="errorMessage" layout="${exception:format=Message}" /> <attribute name="errorStack" layout="${exception:format=StackTrace}" /> <attribute name="message" layout="${event-properties:item=Message:whenEmpty=${message}}" /> <attribute name="applicationName" layout="${appdomain:format={1\}}" /> <attribute name="area" layout="${event-properties:item=Area:whenEmpty=${logger}}" /> </layout> </target> </targets> <rules> <logger name="*" minlevel="Info" writeTo="NewRelicLogFile" /> </rules> </nlog>
问题根源
NLog的Logger.Error(string message, params object[] args)方法接受object[]类型的可变参数。而你的封装方法使用params string[] args,调用时传入的"B1", "B1"会被打包成一个string[]对象,作为单个参数传给NLog的Error方法。此时NLog认为只有1个参数,但消息中有2个占位符,无法匹配,因此不会替换占位符。
解决方案
方案一:修改封装方法的参数类型
将params string[] args改为params object[] args,让参数能正确拆分为多个独立参数传递给NLog:
private static readonly ILogger Logger = LogManager.GetLogger("WebService"); public static void LogError(string message, params object[] args) { Logger.Error(message, args); }
此方案支持NLog的命名占位符({A1}, {A2})和数字索引占位符,调用方式无需修改。
方案二:手动格式化消息后传递
如果需要保留params string[]参数,可先用string.Format完成消息格式化,再传给NLog(注意string.Format仅支持数字索引占位符{0}, {1}):
private static readonly ILogger Logger = LogManager.GetLogger("WebService"); public static void LogError(string message, params string[] args) { var formattedMsg = string.Format(message, args); Logger.Error(formattedMsg); }
调用时需将占位符改为数字索引:
TestClass.LogError("{0}, {1}", "B1", "B1");
补充说明
你的NLog配置中newrelic-jsonlayout的message属性配置${event-properties:item=Message:whenEmpty=${message}}是正常的,只要消息本身被正确格式化,就能正常输出到日志中。
内容的提问来源于stack exchange,提问作者colma

