Установить ValueFormatter для указанного c NLog экземпляра? - PullRequest
0 голосов
/ 18 апреля 2020

У меня есть 2 экземпляра NLog, где для сериализации параметров требуется специальный ValueFormatter. ValueFormatter устанавливается с помощью этого кода:

NLog.Config.ConfigurationItemFactory.Default.ValueFormatter = new NLogValueFormatter();

Так что, как вы можете видеть, он будет применяться ко всем регистраторам. Я не могу найти ни одного свойства на самом NLogger, которое могло бы принимать ValueFormatter.

Есть ли способ привязать этот ValueFormatter только к одному из регистраторов?

Редактировать 1:

private CommunicationFormatProvider provider = new CommunicationFormatProvider();
        public void LogCommunication(string message, params object[] args)
        {
            _comLogger.Log(LogLevel.Info, provider,  message, args);
        }

public class CommunicationFormatProvider : IFormatProvider, ICustomFormatter
{
    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        StringBuilder strBuilder = new StringBuilder();
        strBuilder.Append(format);

        var myTarget = LogManager.Configuration.FindTargetByName("communicationTarget");
        myTarget = ((myTarget as NLog.Targets.Wrappers.WrapperTargetBase)?.WrappedTarget) ?? myTarget;
        var jsonLayout = (myTarget as NLog.Targets.TargetWithLayout)?.Layout as NLog.Layouts.JsonLayout;

        if (jsonLayout?.MaxRecursionLimit > 0)
            strBuilder.Append(JsonConvert.SerializeObject(arg, new JsonSerializerSettings() { MaxDepth = jsonLayout?.MaxRecursionLimit }));

        return strBuilder.ToString();
    }

    object IFormatProvider.GetFormat(Type formatType)
    {
        return (formatType == typeof(ICustomFormatter)) ? this : null;
    }
}

РЕДАКТИРОВАТЬ 2:

public bool FormatValue(object value, string format, CaptureType captureType, IFormatProvider formatProvider, StringBuilder builder)
{

    if (value.GetType() == typeof(LogData))
        return false;

    builder.Append(format);

    try
    {

        var myTarget = LogManager.Configuration.FindTargetByName("communicationTarget");
        myTarget = ((myTarget as NLog.Targets.Wrappers.WrapperTargetBase)?.WrappedTarget) ?? myTarget;
        var jsonLayout = (myTarget as NLog.Targets.TargetWithLayout)?.Layout as NLog.Layouts.JsonLayout;

        if (jsonLayout?.MaxRecursionLimit > 0)
        {
            var jsonSettings = new JsonSerializerSettings() { MaxDepth = jsonLayout?.MaxRecursionLimit };
            using (var stringWriter = new StringWriter())
            {
                using (var jsonWriter = new JsonTextWriterMaxDepth(stringWriter, jsonSettings))
                    JsonSerializer.Create(jsonSettings).Serialize(jsonWriter, value);
                builder.Append(stringWriter.ToString());
            }
        }
        else
            value = null;
    }
    catch(Exception ex)
    {
        builder.Append($"Failed to serlize {value.GetType()} : {ex.ToString()}");
    }
    return true;
}

JSON Сериализатор отсюда: json. net предел maxdepth при сериализация

Ответы [ 2 ]

0 голосов
/ 20 апреля 2020

NLog JsonLayout имеет две опции, которые важны для сериализации свойств LogEvent:

  • IncludeAllProperties
  • MaxRecursionLimit

Параметры по умолчанию примерно такие:

<layout type="JsonLayout" includeAllProperties="false" maxRecursionLimit="0">
   <attribute name="time" layout="${longdate}" />
   <attribute name="level" layout="${level}"/>
   <attribute name="message" layout="${message}" />
</layout>

Но вы также можете активировать включение свойств LogEvent следующим образом:

<layout type="JsonLayout" includeAllProperties="true" maxRecursionLimit="10">
   <attribute name="time" layout="${longdate}" />
   <attribute name="level" layout="${level}"/>
   <attribute name="message" layout="${message}" />
</layout>

Если у вас есть такой объект:

public class Planet
{
    public string Name { get; set; }
    public string PlanetType { get; set; }
    public override string ToString()
    {
        return Name;  // Will be called in normal message-formatting
    }
}

Тогда вы можно зарегистрировать объект следующим образом:

logger.Info("Hello {World}", new Planet() { Name = "Earth", PlanetType = "Water Planet" });

JsonLayout по умолчанию будет просто включать атрибуты по умолчанию, где message -attribute говорит Hello Earth.

Но JsonLayout с includeAllProperties="true" будет включать любые дополнительные свойства LogEvent. И будет включать в себя World -property, который был полностью сериализован.

Идея состоит в том, что не нужно заботиться о том, как настраиваются цели NLog при ведении журнала. Именно Logging-Rules + Target-Configuration + Layout-Configuration решает, как все должно быть наконец написано.

Если вы действительно хотите, чтобы объект был сериализован в ${message}, то вы также можете сделать это:

logger.Info("Hello {@World}", new Planet() { Name = "Earth", PlanetType = "Water Planet" });

Если вы не хотите, чтобы свойства LogEvent смешивались со свойствами по умолчанию, вы можете сделать это:

<layout type="JsonLayout" maxRecursionLimit="10">
   <attribute name="time" layout="${longdate}" />
   <attribute name="level" layout="${level}"/>
   <attribute name="message" layout="${message}" />
   <attribute name="properties" encode="false">
      <layout type="JsonLayout" includeAllProperties="true" maxRecursionLimit="10" />
   </attribute>
</layout>

Если у вас есть объект, который смешивает поля с свойств, то вы можете указать NLog выполнить пользовательское отражение для этого типа:

LogManager.Setup().SetupSerialization(s =>
   s.RegisterObjectTransformation<GetEntityViewRequest>(obj => 
       return Newtonsoft.Json.Linq.JToken.FromObject(obj) // Lazy and slow
   )
);
0 голосов
/ 18 апреля 2020

Вы можете убедиться, что форматируемый объект наследуется от IFormattable. https://docs.microsoft.com/en-us/dotnet/api/system.iformattable

Затем вы можете реализовать различные форматеры, где каждый регистратор может выбрать свой любимый при регистрации вашего специального объекта.

Подобно этому форматеру, который обеспечивает специальное форматирование Объекты-исключения:

https://github.com/NLog/NLog/blob/dev/src/NLog/Internal/ExceptionMessageFormatProvider.cs

Затем просто используйте параметр IFormatProvider formatProvider при вызове методов в Logger, при регистрации вашего специального объекта.

Полагаю, вы могли бы заставить свой собственный NLogValueFormatter выполнять форматирование по умолчанию. И Logger, который нуждается в специальном дополнительном форматировании, может затем предоставить свой собственный IFormatProvider.

Обновить ответ Знаете ли вы, что вы можете заставить NLog автоматически выполнять JsonSerialization объекта, используя сообщение -template:

Это выполнит операцию ToString по умолчанию:

logger.Info("Hello {World}", new { Name = "Earth", Type = "Water Planet" });

Это будет сигнализировать, что объект безопасен для отражения (Обратите внимание на @) :

logger.Info("Hello {@World}", new { Name = "Earth", Type = "Water Planet" });

См. Также: https://github.com/NLog/NLog/wiki/How-to-use-structured-logging

...