【问题标题】:Is it possible to use NLog's structured logging without having templated messages?是否可以在没有模板消息的情况下使用 NLog 的结构化日志记录?
【发布时间】:2019-06-18 08:44:01
【问题描述】:

直到今天,我们一直在使用 NLog 版本 4.4.12(没有结构化日志记录)。但是,我们使用 https://www.nuget.org/packages/NLog.StructuredLogging.Json/ 进行结构化日志记录。

使用这个扩展的好处是你不需要模板化的消息(包含索引或占位符,用于记录其他参数/​​对象)。该消息不包含任何索引或占位符供您记录的其他对象(即匿名类型)。

切换到支持开箱即用结构化日志记录的 NLog 4.6.5,我们希望摆脱额外的 NuGet 包。但是,只有在使用带有实际索引/命名占位符的 模板化 消息时,才会记录我们的附加参数。

我们的消息中没有索引或占位符确实会导致我们的其他参数/​​对象无法通过 JSON 呈现。

是否可以有非模板化的消息,但仍使用 NLog 的结构化日志记录我们传递给它们以添加到 JSON 中的附加参数?

以下是一个示例(请注意,我们在 nlog 周围使用了额外的包装器)

NLog 版本:4.6.5

平台:.Net 4.5

当前的 NLog 配置

// Arrange
var typeUsingLogger = typeof(NLogWrapperTest);
var nLogWrapper = new NLogWrapper(typeof(NLogWrapper));

var level = (LogLevel)Enum.Parse(typeof(LogLevel), nLevel.Name);
var message = $"{Guid.NewGuid()}"; // {{extendedLogProperties}}  {{@extendedLogProperties}} {{@purchase}} {{badplaceholder}}
var innerException = new DivideByZeroException("bla inner exception");
var exception = new ArgumentNullException("bla out exception", innerException);
var extendedLogProperties = new
{
    ClientID = 8,
    MyOtherProp = "abc",
    MySubObject = new
    {
        //nested object although not recommended
        A = 123,
        B = "yep"
    }
};

//log configuration
var logConfig = new LoggingConfiguration();

var memoryTarget = new MemoryTarget("MemoryTarget");
var jsonLayout = new JsonLayout
{
    IncludeAllProperties = true,
    Attributes =
    {
            new JsonAttribute("dateTime", "${date:universalTime=true:format=o}" ),
            new JsonAttribute("level", "${level:uppercase=true}" ),
            new JsonAttribute("logger", "${logger}" ),
            new JsonAttribute("message", "${message}" ),
            new JsonAttribute("callsite", "${callsite:className=true:methodName=true:skipFrame=0}" ),
            new JsonAttribute("exception", "${exception:format=ToString:innerFormat=ToString}" ),
            new JsonAttribute("machinename", "${machinename}" ),
            new JsonAttribute("processid", "${processid}" ),
            new JsonAttribute("threadid", "${threadid}" ),
            new JsonAttribute("threadname", "${threadname}" ),
            new JsonAttribute("application", "${application}" ),
            new JsonAttribute("aspnetSessionId", "${aspnet-sessionid}" ),
            new JsonAttribute("iisSiteName", "${iis-site-name}" ),
            new JsonAttribute("stage", "${stage}" ),
    }
};
memoryTarget.Layout = jsonLayout;
logConfig.AddTarget("memoryTarget", memoryTarget);
var memoryTargetLoggingRule = new LoggingRule("*", nLevel, memoryTarget);
logConfig.LoggingRules.Add(memoryTargetLoggingRule);

LogManager.Configuration = logConfig;

// Act
nLogWrapper.Log(level, message, typeUsingLogger, exception, extendedLogProperties);

var jsonLogMsg = memoryTarget.Logs[0];
Assert.Matches("ClientID", jsonLogMsg);

我们为什么需要它?

  • 如果不使用任何替换的索引或占位符来保持消息不变,这样我们就可以在日志中搜索完全相同的消息,这真是太好了。 (不能使用new JsonAttribute("message", "${message:raw=true}"

  • 同样这样,我们最终不会在日志消息中包含 JSON 序列化对象(替换模板化消息的占位符/索引)以及这些附加参数的附加 JSON 字段。

    李>

请查看其最佳实践:https://github.com/justeat/NLog.StructuredLogging.Json/blob/master/README.md#best-practices

如果您问:“为什么不继续使用 NuGet NLog 扩展?” 答案是,在嵌套对象的模板化消息中使用 {@placeholder} 时,NLog 的结构化日志可以更好地呈现附加参数。

编辑 1: 我想让我的匿名对象的所有属性都呈现在 json 的根目录中。如:

{
    ...
    "ClientID": 8,
    "MyOtherProp": "abc",
    "MySubObject": {              
                    "A": 123,
                    "B": "yep"
                },
    ...
}

【问题讨论】:

  • 我对你的(长)“当前 NLog 配置”有点困惑。不是配置吗?如果您可以显示记录器调用(单行,而不是 nLogWrapper.Log(level, message, typeUsingLogger, exception, extendedLogProperties))和所需的输出,我会很有帮助。

标签: json nlog structured-logging


【解决方案1】:

我认为您正在寻找logger.WithProperty

例子:

var extendedLogProperties = new
{
    ClientID = 8,
    MyOtherProp = "abc",
    MySubObject = new
    {
        //nested object although not recommended
        A = 123,
        B = "yep"
    }
};

logger.WithProperty("extendedLogProperties", extendedLogProperties).Info("test message");

您可以将其序列化为 JSON、XML 等。

示例,包含所有属性的 JSON

将所有事件属性呈现为 JSON

配置:

 <target xsi:type="File" name="jsonFile" fileName="c:\temp\nlog-json-nested-${shortdate}.log">
     <layout type="JsonLayout">
         <attribute name="time" layout="${longdate}" />
         <attribute name="level" layout="${level}" />
         <attribute name="message" layout="${message}" />
         <attribute name="eventProperties" encode="false" >
             <layout type='JsonLayout' includeAllProperties="true"  maxRecursionLimit="2"/>
         </attribute>
     </layout>
 </target>

这里重要的是includeAllProperties="true"maxRecursionLimit="2"(默认是0)。见Json Layout docs

这应该呈现(很好地格式化为演示,但将是一行):

{
    "time": "2019-06-18 11:09:00.2349",
    "level": "Info",
    "message": "test message",
    "eventProperties": {
        "extendedLogProperties": {
            "ClientID": 8,
            "MyOtherProp": "abc",
            "MySubObject": {
                "A": 123,
                "B": "yep"
            }
        }
    }
}

注意事项

所以要清楚:

JSON 将写在一行上,所以没有换行符。

【讨论】:

  • 感谢您的回答!但是我想避免创建一个新的记录器(通过一直调用 WithProperties)以及我想在 json 的根目录中拥有所有匿名类型的字段(这样我就可以使用 Kibana 轻松搜索它们(ELK ))。很抱歉在这里不清楚。
  • 我设法实现了这一点。感谢@Julian 让我朝着正确的方向前进
  • 如何让 JSON 写成多行和缩进?
  • @KevinBui 您在登录时通常不会这样做。阅读日志时,您只关心多行/缩进。为此,您可以将日志拉入 JSON 查看器,该查看器将为您处理美化 JSON。
【解决方案2】:

在我的包装中,我设法像这样实现它:

        private void Log(
            NLog.LogLevel nlogLogLevel,
            Logger nlogLogger,
            Type typeUsingLogger,
            string message,
            Exception exception,
            IDictionary<string, object> additionalProperties = null)
        {
            var logEventInfo = new LogEventInfo(nlogLogLevel, typeUsingLogger.ToString(), null, message, new object[] { }, exception);
            if (additionalProperties != null)
            {
                foreach (var x in additionalProperties)
                {
                    if (!logEventInfo.Properties.ContainsKey(x.Key))
                    {
                        logEventInfo.Properties.Add(x.Key, x.Value);
                    }
                }
            }

            nlogLogger.Log(_logWrapperType, logEventInfo);
        }

并将 includeAllProperties 设置为 true 以及将 ma​​xRecursionLimit 设置得更高 (2)

【讨论】:

    猜你喜欢
    • 2018-03-24
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-18
    • 2023-01-12
    相关资源
    最近更新 更多