Можно ли использовать структурированное ведение журнала NLog без шаблонных сообщений? - PullRequest
0 голосов
/ 18 июня 2019

До сегодняшнего дня мы использовали 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 ]

1 голос
/ 18 июня 2019

В моей обертке мне удалось реализовать это так:

        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)

1 голос
/ 18 июня 2019

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

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>

Здесь важно 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 будет записан в одну строку, поэтому без новой строки.

...