使用 NLog Layout Renders 将数值记录为数字

Logging numeric values as numbers using NLog Layout Renders

我们可以使用 NLog Layout Renders 在我们的日志中添加自定义字段。这是我的配置文件:

<configuration>
  <configSections>
    <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
  </configSections>
  <nlog>
    <targets async="true">
      <target
        name="clp"
        type="Console">
        <layout type="JsonLayout">
          <attribute name="duration" layout="${aspnet-item:variable=RequestDuration}" />
        </layout>
      </target>
    </targets>
    <rules>
      <logger name="*" writeTo="clp" minlevel="Trace" />
    </rules>
  </nlog>
</configuration>

aspnet-item:variable=RequestDuration的值是在我的日志中间件中计算出来的:

public class RequestLoggingMiddleware
{
    public async Task Invoke(HttpContext context)
    {
        var startTime = this.clock.UtcNow;

        await this.next.Invoke(context);

        var endTime = this.clock.UtcNow;
        var requestDuration = (long)(endTime - startTime).TotalMilliseconds;
        context.Items["RequestDuration"] = requestDuration;
    }
}

在我的 Startup 中注册的喜欢:

app.UseMiddleware<RequestLoggingMiddleware>();

这是我配置 NLog 的方式:

public static IWebHostBuilder CreateHostBuilder(string[] args)
{
    return WebHost.CreateDefaultBuilder(args).UseStructureMap().UseStartup<Startup>().ConfigureAppConfiguration(AppConfig)
        .ConfigureLogging(
            (context, logging) =>
                {
                    LogManager.LoadConfiguration($"nlog.{context.HostingEnvironment.EnvironmentName}.config");
                    logging.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true });
                    logging.ClearProviders();
                    logging.SetMinimumLevel(LogLevel.Trace);
                }).UseNLog();
}

如果我记录如下内容:

Logger.Info("this is just a test");

它将在控制台输出中显示如下:

{ "duration": "123"}

即虽然 aspnet-item:variable=RequestDuration 的值很长,但持续时间将呈现为字符串。但是,我希望它被记录为一个数字。 (我们使用 ELK 堆栈,并且能够将自定义值记录为数字将允许我们添加过滤器,例如,在 Kibana 中此类字段的值范围)

应用程序和包版本

对于普通文件日志,没有类型,因此 My custom string42 将被打印为那些值:My custom string42(不要不要对文档中的引号感到困惑)

一些布局支持类型,例如 JSON 布局。

例如:

<nlog>
  <targets>
    <target name='jsonFile' type='File' fileName='log.json'>
      <layout type='JsonLayout'>
        <attribute name='time' layout='${longdate}' />
        <attribute name='level' layout='${level:upperCase=true}'/>
        <attribute name='answer' layout='${event-properties:item=TheAnswer}'/>
      </layout>
    </target>
  </targets>
  <rules>
      <logger name="*" minlevel="Debug" writeTo="jsonFile" />
  </rules>
</nlog>

会导致这个 JSON:

{ "time": "2021-02-11 01:04:55.0000", "level": "INFO", "answer": 42 }

旁注,记录器调用

logger调用现在推荐这样写:

Logger logger = LogManager.GetCurrentClassLogger(); // static field recommend

logger.Info("The real answer is {TheAnswer}", 42); 

或者如果您不需要消息中的 42 (${message}) - NLog 4.6.3+

logger.WithProperty("TheAnswer", 42).Info("Hello There"); 

JsonLayout属性有个小技巧,一次可以指定encode="false"。通常在Attribute-Layout使用JsonLayout时使用。

    <layout type="JsonLayout">
      <attribute name="duration" layout="${aspnet-item:variable=RequestDuration}" encode="false" />
    </layout>

如果项目 RequestDuration 保证为整数或空,则它将生成不带引号的 json-属性。它将被解析为整数,而不是字符串。

NLog 5.0 将对此进行改进,因为属性将获得 ValueType-属性,因此可以指定字符串类型以外的其他类型(并且还可以验证值类型)。而不是使用 encode="False".

的技巧