You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

封装Logger.Error方法后,NLog为何无法动态替换占位符?

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:44:52