We're using log4net.ext.json to serialize our logs to a file in JSON format. However, I can't figure out how to add optional fields to the root of the json object without adding unnecessary data to logs where that data doesn't appear. Let me explain:
We have a few different types of logs, each of which has its own custom renderer and log object. What we'd like is to have data from the these objects appear in the base of the JSON object as that matches our multi-platform logging standards.
Here's an example: we have an HttpData object we use when logging inbound and outbound requests and responses to our web service. In our appender, we take the HttpData object and put its property values (such as the HttpRoute field) into the LoggingEvent properties object.
We then configure the appender as follows:
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="D:\log.txt"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="100MB"/>
<AppendToFile value="true"/>
<layout type="log4net.Layout.SerializedLayout, log4net.Ext.Json">
...
<member value="HttpRoute:HttpRoute"/>
...
</layout>
</appender>
The SerializedLayout is smart enough to pull the value of HttpRoute from the loggingEvent properties, and produce JSON that looks like:
{
...,
"HttpRoute": "/my/route/to/the/api",
...
}
This is perfect! However, when HttpRoute is not found in the LoggingEvent properties, it then assumes that the specified field is a string literal, and produces this:
{
...,
"HttpRoute": "HttpRoute",
...
}
This is less than desirable.
Is there a way to get HttpRoute to only appear when it has a value in the logging event properties? Or is there an alternate way to get the fields from the logging object out to the root of the logged JSON object?
Thanks for the help.
I hear you now.
Have you tried?
<member value="HttpRoute"/>
I found the culprit in Member.cs:
/// <param name="loggingEvent">the event to get value from</param>
/// <param name="obj">value found</param>
/// <returns>success of finding a NestedLayout not null</returns>
protected virtual bool GetDefaultValue(Core.LoggingEvent loggingEvent, out object obj)
{
obj = Option ?? Name;
return true;
}
The quick fix for you is to override that method and return just Option
, not ?? Name
:
protected override bool GetDefaultValue(Core.LoggingEvent loggingEvent, out object obj)
{
obj = Option;
return true;
}
Then you'll need to declare your new Member type in the configuration:
<member value="HttpRoute:HttpRoute" type="Your.Member"/>
If you think this should be the default, let's have a chat over the enhancement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With