Можно ли использовать структурированное ведение журнала NLog без шаблонных сообщений?

До сегодняшнего дня мы использовали 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?" Ответ заключается в том, что структурированное ведение журнала в NLog делает более приятными дополнительные параметры при использовании {@placeholder} в шаблонных сообщениях для вложенных объектов.

Редактировать 1: я хотел бы, чтобы все свойства моего анонимного объекта отображались в корне json. Такие как:

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

2 ответа

Решение

Я думаю, что вы ищете 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

Это должно отрисоваться (красиво отформатировано для демонстрации, но будет одной строкой):

{
    "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 будет записан в одну строку, поэтому нет новых строк.

В моей обертке мне удалось понять это так:

        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, а также установка maxRecursionLimit выше (2)

Другие вопросы по тегам