C# 是否为特定NLog实例设置ValueFormatter?

C# 是否为特定NLog实例设置ValueFormatter?,c#,.net,nlog,formatter,C#,.net,Nlog,Formatter,我有两个NLog实例,其中一个需要一个特殊的ValueFormatter来序列化参数。ValueFormatter使用以下代码设置: NLog.Config.ConfigurationItemFactory.Default.ValueFormatter = new NLogValueFormatter(); 因此,正如您所看到的,它将应用于所有伐木工人。我在NLogger上找不到任何可能需要ValueFormatter的属性 有没有办法将此ValueFormatter仅绑定到其中一个记录器 编

我有两个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序列化程序:您可以确保要格式化的对象继承自
IFormattable

然后,您可以实现不同的格式化程序,每个记录器可以在记录特殊对象时选择自己的收藏夹

与此格式化程序类似,此格式化程序提供异常对象的特殊格式:

然后,在记录特殊对象时,在记录器上调用方法时,只需确保使用
IFormatProvider formatProvider
-参数

我想您可以自定义
NLogValueFormatter
执行默认格式。需要特殊附加格式化的记录器可以提供自己的自定义
IFormatProvider

更新答案您是否知道可以使用消息模板强制NLog自动执行对象的JsonSerialization:

这将执行默认的ToString操作:

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

这将表明对象可以安全反射(请注意
@
):

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


另请参见:

NLog JsonLayout有两个选项,这两个选项对于LogEvent属性的序列化非常重要:

  • IncludealProperty
  • 最大递归极限
默认参数如下所示:

<layout type="JsonLayout" includeAllProperties="false" maxRecursionLimit="0">
   <attribute name="time" layout="${longdate}" />
   <attribute name="level" layout="${level}"/>
   <attribute name="message" layout="${message}" />
</layout>
<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" });
然后您可以像这样记录对象:

<layout type="JsonLayout" includeAllProperties="false" maxRecursionLimit="0">
   <attribute name="time" layout="${longdate}" />
   <attribute name="level" layout="${level}"/>
   <attribute name="message" layout="${message}" />
</layout>
<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将只包括默认属性,其中
消息
-属性表示
你好地球

但是带有
includealProperties=“true”
的JsonLayout将包括任何其他LogEvent属性。并将包括已完全序列化的
World
-属性

其思想是,在记录时不应关心非直瞄目标的配置。日志规则+目标配置+布局配置决定了最终应该如何编写

如果确实希望将对象序列化为
${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
   )
);
LogManager.Setup().SetupSerialization(s=>
s、 寄存器对象转换(obj=>
返回Newtonsoft.Json.Linq.JToken.FromObject(obj)//懒惰且缓慢
)
);

谢谢,我创建了一个IFormatProvider实现,它是在调用ILogger.Log(LogLevel、IFormatProvider、string object[])时提供的,但从未触发该格式?请参见编辑。我想您需要显示更多的代码。如何将实现ICustomFormatter的对象传递给记录器?您是否自定义对象实现了
ifformattable
?@Banshee Btw。您实现的代码看起来非常奇怪(在格式化程序中查找目标配置)。我认为你应该提出一个新的问题,问你如何达到最终目标。我想你正在走一条通往目的地的非常奇怪的路,并且正在克服不应该克服的障碍。默认情况下,只有时间、操作和客户端将被记录到磁盘上,但我需要能够在运行时设置是否要添加参数,以及这些参数的设置深度。因为它需要在运行时,所以每次都需要读取设置。我想我可以修正一下,这样每次通话只能读一次,但这不是现在最重要的部分。此时,我只需要一个rout,在这里我可以指出,这个特定的记录器将根据特定的方法格式化其输出。谢谢,您在另一篇文章“NLog仅在默认情况下序列化对象属性(当具有适当的getter时)。它忽略对象字段。”中写道,这就是我需要手动序列化对象的原因。上面我的文章中的代码按照我的需要工作,它提供了正确的数据,但我理解我的方法不是故意的。那么,如何从配置中为1个特定记录器定制maxRecursionLimit序列化?请确保特殊记录器命中配置为具有
maxRecursionLimit=1
的目标,并且该目标的布局将进行反射。您可以为自己的类型(包含字段)自定义反射,如:@Banshee,因此您的问题不是“如何使用配置中的maxRecursionLimit为1个特定记录器自定义序列化?”而是“如何使用字段自定义对象的序列化?”@Banshee,但是的,这个答案几乎是我重复的。您为什么不想调用NLog
RegisterObjectTransformation
?我们得到了大量包含字段、默认值和属性的复杂对象,GetEntityViewRequest只是其中之一。所有这些都需要以一种通用的方式进行序列化,我们可以在运行时设置深度。这也需要仅在处理通信的NLogger上设置,而不是在我们的其他NLogger上设置。我可以在我的第一篇文章中进行选择,以尊重配置文件的深度,但听起来这是一种糟糕的方式。那么,在不操纵数据模型的情况下,什么样的方法最适合我们的数据模型呢?注意,我们所有的类都是带有DataMembers.Btw的DataContracts
JsonSerializerSettings.MaxDepth
在此处无效。它仅在“阅读”时使用,而不是