简体   繁体   English

是否可以在没有模板消息的情况下使用NLog的结构化日志记录?

[英]Is it possible to use NLog's structured logging without having templated messages?

Until today we have used NLog version 4.4.12 (without structured logging). 直到今天,我们仍使用NLog 4.4.12版(不使用结构化日志记录)。 However we were using https://www.nuget.org/packages/NLog.StructuredLogging.Json/ for structured logging. 但是,我们使用https://www.nuget.org/packages/NLog.StructuredLogging.Json/进行结构化日志记录。

The nice thing about using this extension is that you do NOT need to have templated messages (containing indexes or placeholders for your additional parameters/object(s) to log). 使用此扩展的好处是您不需要模板消息(包含索引或占位符以记录其他参数/​​对象)。 The message does not contain any indexes or placeholders for your additional objects (ie anonymous type) to log. 该消息不包含要记录的其他对象(即匿名类型)的任何索引或占位符。

Switching to NLog 4.6.5 that supports structured logging out of the box we would like to get rid of that additional NuGet package. 切换到支持结构化登录的NLog 4.6.5时,我们希望摆脱该附加的NuGet包。 However our additional parameters are only logged when using templated messages with actual indexed/named placeholders. 但是,仅当使用带有实际索引/命名占位符的模板消息时,才会记录我们的附加参数。

Not having index or placeholders in our message does result in NOT having our additional parameters/objects to be rendered out via JSON. 我们的消息中没有索引或占位符会导致没有其他参数/​​对象通过JSON呈现出来。

Is it possible to have non-templated messages however still using NLog's structured logging for our additional parameters that have been passed for them to being added in JSON? 是否有可能使用非模板消息,但是仍使用NLog的结构化日志记录我们传递给他们的其他参数,以便将它们添加到JSON中?

Below is an example (please note that we are using an additional wrapper around nlog) 下面是一个示例(请注意,我们在nlog周围使用了额外的包装器)

NLog version : 4.6.5 NLog版本 :4.6.5

Platform : .Net 4.5 平台 :.Net 4.5

Current NLog config 当前的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);

Why do we need it? 我们为什么需要它?

  • It would be really nice to have the message unchanged without having any replaced indexes or placeholders so that we can search for the exact same message in our logs. 如果没有任何替换的索引或占位符,则使消息不变就好了,这样我们就可以在日志中搜索完全相同的消息。 (using new JsonAttribute("message", "${message:raw=true}" is not an option) (不能使用new JsonAttribute("message", "${message:raw=true}"

  • Also this way we do not end up in having the JSON serialized objects once in the log message (replacing the templated message's placeholders/indexes) AND the additional JSON fields for these additional parameters. 同样,通过这种方式,我们也不会最终在日志消息(替换模板消息的占位符/索引)和这些附加参数的附加JSON字段中使JSON序列化对象一次。

Please have a look at its best practices: https://github.com/justeat/NLog.StructuredLogging.Json/blob/master/README.md#best-practices 请查看其最佳做法: https : //github.com/justeat/NLog.StructuredLogging.Json/blob/master/README.md#best-practices

If you ask: "Why don't you continue using the NuGet NLog extension?" 如果您问:“为什么不继续使用NuGet NLog扩展?” The answer is that NLog's structured logging renders out the additional parameters much nicer when using {@placeholder} in templated messages for nested objects. 答案是,当在嵌套对象的模板消息中使用{@placeholder}时,NLog的结构化日志会更好地呈现其他参数。

Edit 1: I would like to have all the properties of my anonymous object to be rendered in the root of the json. 编辑1:我想将匿名对象的所有属性呈现在json的根目录中。 Such as: 如:

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

I think your are looking for logger.WithProperty . 我认为您正在寻找logger.WithProperty

Example: 例:

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");

and you could serialize that to JSON, XML etc. 您可以将其序列化为JSON,XML等。

Example, JSON with all properties 示例,带有所有属性的JSON

Render all event properties as JSON 将所有事件属性呈现为JSON

config: 配置:

 <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>

Important is here includeAllProperties="true" and maxRecursionLimit="2" (default is 0 ). 重要的是includeAllProperties="true"maxRecursionLimit="2" (默认为0 )。 See Json Layout docs 请参阅Json Layout文档

This should render (nicely formatted for demo, but will be one one line): 这应该呈现(演示的格式很好,但是将是一行):

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

Notes 笔记

So to be clear: 因此要明确:

The JSON will be written on one line, so no newlines. JSON将写在一行上,因此没有换行符。

In my wrapper i managed to realize it like this: 在我的包装中,我设法实现了这样的目标:

        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);
        }

and setting includeAllProperties to true as well as setting maxRecursionLimit higher (2) 并将includeAllProperties设置为true ,并将maxRecursionLimit设置更高(2)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 NLog:记录序列化为JSON的对象 - NLog: logging an object serialized to JSON "是否有人类可读的结构化日志记录格式?" - Is there a human readable structured logging fomat? 使用nlog编写休息服务 - use nlog to write to rest service 是否可以在不将string / js对象存储在文件中的情况下上传它? -IPFS - Is it possible to upload a string/js object without having it stored in a file? - IPFS 现在可以使用BreezeJS而无需使用EF吗? - Can now use BreezeJS without having to use EF? 如何以编程方式在 Stackdriver 中使用 NLog 和 JsonPayload - How to use NLog with JsonPayload in Stackdriver Programmatically 是否可以在不安装Play的情况下使用JSON库? - Is it possible to use JSON library without installing Play? Serilog 不生成有效的 json(结构化日志记录) - Serilog doesn't produce a valid json (structured logging) Python - 是否可以获得 logging.handlers.httpHandler 以将内容 header 设置为 JSON 而无需使用自定义代码? - Python - is it possible to get logging.handlers.httpHandler to set the content header to JSON without resorting to custom code? 有没有办法在没有数据库的情况下使用测试Ajax / JSON / XML吗? - Is there a way to use test Ajax / JSON/ XML without having a database?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM