将 NLog 属性布局为 JSON?
Layout NLog properties as JSON?
我想弄清楚如何将 LogEventInfo
对象中的所有属性记录到 JSON 格式的字符串中。根据 issue on github,我尝试这样做:
<target xsi:type="ColoredConsole" name="coloredConsole">
<layout xsi:type="JsonLayout">
<attribute name="timestamp" layout="${longdate}"/>
<attribute name="level" layout="${level:uppercase=true}"/>
<attribute name="exception" layout="${onexception:${exception:format=tostring}}" />
<attribute name="properties" encode="false">
<layout type="JsonLayout">
<attribute name="properties" layout="${all-event-properties}" />
</layout>
</attribute>
</layout>
</target>
...但不幸的是,我的属性包含复杂的对象(我有两个名称为 "properties" 和 "tags" 的属性,其中 "properties" 是 IDictionary<string, object>
"tags" 是 LogEventInfo.Properties
属性) 中的 IList<string>
,根本不序列化。我最终得到了类似这样的东西:
{ "timestamp": "2017-05-18 08:41:28.7730", "level": "INFO", "properties": { "properties": "properties=System.Collections.Generic.Dictionary`2[System.String,System.Object], tags=System.Collections.Generic.List`1[System.String]" } }
我期待(并希望)一个序列化的 JSON 字典,它会给我日志消息的上下文,但显然这不是我得到的。
如何正确序列化我的 LogEventInfo
对象中的属性?
嗯,看起来 NLog 中有一个错误,所以我有点制作了自己的渲染器。这就是我所做的。首先,我使用 JSON.NET:
做了一个扩展方法
public static string ToJson(this object obj, bool format = false, string dateFormat = null)
{
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
if (!String.IsNullOrWhiteSpace(dateFormat))
{
settings.Converters = new List<JsonConverter>
{
new IsoDateTimeConverter {DateTimeFormat = dateFormat}
};
return JsonConvert.SerializeObject(obj, format ? Formatting.Indented : Formatting.None, settings);
}
return JsonConvert.SerializeObject(obj, format ? Formatting.Indented : Formatting.None, settings);
}
...接下来,我创建了一个 LayoutRenderer,如下所示:
[LayoutRenderer("json-event-properties")]
public class JsonEventPropertiesLayoutRenderer : LayoutRenderer
{
/// <summary>
/// Renders the specified environmental information and appends it to the specified <see cref="T:System.Text.StringBuilder" />.
/// </summary>
/// <param name="builder">The <see cref="T:System.Text.StringBuilder" /> to append the rendered data to.</param>
/// <param name="logEvent">Logging event.</param>
protected override void Append(StringBuilder builder, LogEventInfo logEvent) {
if (logEvent.Properties == null || logEvent.Properties.Count == 0)
return;
var serialized = logEvent.Properties.ToJson();
builder.Append(serialized);
}
}
在我的应用程序中,启动时,我像这样注册了我的 LayoutRenderer
:
LayoutRenderer.Register<JsonEventPropertiesLayoutRenderer>("json-event-properties");
...最后,我这样配置了 NLog 目标:
<layout xsi:type="JsonLayout">
<attribute name="timestamp" layout="${longdate}"/>
<attribute name="level" layout="${level:uppercase=true}"/>
<attribute name="exception" layout="${onexception:${exception:format=tostring}}" />
<attribute name="message" layout="${message}" />
<attribute name="properties" layout="${json-event-properties}" encode="false"/>
</layout>
运行 像这样,JSON 格式正确,我可以访问 LogEventInfo
对象中的属性。
这里使用 NLog 4.5.1。
使用此配置:
<target xsi:type="File" name="jsonFile2" 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="20"/>
</attribute>
</layout>
</target>
此代码:
var nestedData = new
{
A = "a value",
B = "b value",
Nested = new
{
A = "nested a value",
B = "nested b value",
},
Ints = new Collection<int> { 1, 2, 3},
Dictionary = new Dictionary<string, object>
{
{"nested", new { X= 'x', y = 'y' }},
{ "awesome", "nlog"}
}
};
LogEventInfo eventInfo = new LogEventInfo
{
Level = LogLevel.Info,
Properties = { {"nestedData", nestedData } }
};
logger.Log(eventInfo);
输出:
{
"time":"2018-04-05 18:08:01.0813",
"level":"INFO",
"eventProperties":{
"nestedData":{
"A":"a value",
"B":"b value",
"Nested":{
"A":"nested a value",
"B":"nested b value"
},
"Ints":[
1,
2,
3
],
"Dictionary":{
"nested":{
"X":"x",
"y":"y"
},
"awesome":"nlog"
}
}
}
}
实际上,它打印了丑陋的 one-line 版本。
参见带有结构化日志记录的嵌套 JSON on the NLog wiki
注意maxRecursionLimit
设置。默认情况下它是 0,表示 "No object reflection",这意味着您将获得事件属性的 ToString()
表示。
我想弄清楚如何将 LogEventInfo
对象中的所有属性记录到 JSON 格式的字符串中。根据 issue on github,我尝试这样做:
<target xsi:type="ColoredConsole" name="coloredConsole">
<layout xsi:type="JsonLayout">
<attribute name="timestamp" layout="${longdate}"/>
<attribute name="level" layout="${level:uppercase=true}"/>
<attribute name="exception" layout="${onexception:${exception:format=tostring}}" />
<attribute name="properties" encode="false">
<layout type="JsonLayout">
<attribute name="properties" layout="${all-event-properties}" />
</layout>
</attribute>
</layout>
</target>
...但不幸的是,我的属性包含复杂的对象(我有两个名称为 "properties" 和 "tags" 的属性,其中 "properties" 是 IDictionary<string, object>
"tags" 是 LogEventInfo.Properties
属性) 中的 IList<string>
,根本不序列化。我最终得到了类似这样的东西:
{ "timestamp": "2017-05-18 08:41:28.7730", "level": "INFO", "properties": { "properties": "properties=System.Collections.Generic.Dictionary`2[System.String,System.Object], tags=System.Collections.Generic.List`1[System.String]" } }
我期待(并希望)一个序列化的 JSON 字典,它会给我日志消息的上下文,但显然这不是我得到的。
如何正确序列化我的 LogEventInfo
对象中的属性?
嗯,看起来 NLog 中有一个错误,所以我有点制作了自己的渲染器。这就是我所做的。首先,我使用 JSON.NET:
做了一个扩展方法public static string ToJson(this object obj, bool format = false, string dateFormat = null)
{
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
if (!String.IsNullOrWhiteSpace(dateFormat))
{
settings.Converters = new List<JsonConverter>
{
new IsoDateTimeConverter {DateTimeFormat = dateFormat}
};
return JsonConvert.SerializeObject(obj, format ? Formatting.Indented : Formatting.None, settings);
}
return JsonConvert.SerializeObject(obj, format ? Formatting.Indented : Formatting.None, settings);
}
...接下来,我创建了一个 LayoutRenderer,如下所示:
[LayoutRenderer("json-event-properties")]
public class JsonEventPropertiesLayoutRenderer : LayoutRenderer
{
/// <summary>
/// Renders the specified environmental information and appends it to the specified <see cref="T:System.Text.StringBuilder" />.
/// </summary>
/// <param name="builder">The <see cref="T:System.Text.StringBuilder" /> to append the rendered data to.</param>
/// <param name="logEvent">Logging event.</param>
protected override void Append(StringBuilder builder, LogEventInfo logEvent) {
if (logEvent.Properties == null || logEvent.Properties.Count == 0)
return;
var serialized = logEvent.Properties.ToJson();
builder.Append(serialized);
}
}
在我的应用程序中,启动时,我像这样注册了我的 LayoutRenderer
:
LayoutRenderer.Register<JsonEventPropertiesLayoutRenderer>("json-event-properties");
...最后,我这样配置了 NLog 目标:
<layout xsi:type="JsonLayout">
<attribute name="timestamp" layout="${longdate}"/>
<attribute name="level" layout="${level:uppercase=true}"/>
<attribute name="exception" layout="${onexception:${exception:format=tostring}}" />
<attribute name="message" layout="${message}" />
<attribute name="properties" layout="${json-event-properties}" encode="false"/>
</layout>
运行 像这样,JSON 格式正确,我可以访问 LogEventInfo
对象中的属性。
这里使用 NLog 4.5.1。
使用此配置:
<target xsi:type="File" name="jsonFile2" 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="20"/>
</attribute>
</layout>
</target>
此代码:
var nestedData = new
{
A = "a value",
B = "b value",
Nested = new
{
A = "nested a value",
B = "nested b value",
},
Ints = new Collection<int> { 1, 2, 3},
Dictionary = new Dictionary<string, object>
{
{"nested", new { X= 'x', y = 'y' }},
{ "awesome", "nlog"}
}
};
LogEventInfo eventInfo = new LogEventInfo
{
Level = LogLevel.Info,
Properties = { {"nestedData", nestedData } }
};
logger.Log(eventInfo);
输出:
{
"time":"2018-04-05 18:08:01.0813",
"level":"INFO",
"eventProperties":{
"nestedData":{
"A":"a value",
"B":"b value",
"Nested":{
"A":"nested a value",
"B":"nested b value"
},
"Ints":[
1,
2,
3
],
"Dictionary":{
"nested":{
"X":"x",
"y":"y"
},
"awesome":"nlog"
}
}
}
}
实际上,它打印了丑陋的 one-line 版本。
参见带有结构化日志记录的嵌套 JSON on the NLog wiki
注意maxRecursionLimit
设置。默认情况下它是 0,表示 "No object reflection",这意味着您将获得事件属性的 ToString()
表示。