Skip to content

Custom PatternLayoutConverter with log4net.Ext.Json?

I have the following log4net configuration:

<log4net>
    <appender name="Console" type="log4net.Appender.ConsoleAppender">
        <layout type='log4net.Layout.SerializedLayout, log4net.Ext.Json'>
            <renderer type='log4net.ObjectRenderer.JsonDotNetRenderer, log4net.Ext.Json.Net'>
                <DateFormatHandling value="IsoDateFormat" />
                <NullValueHandling value="Ignore" />
            </renderer>
            <converter>
              <name value="preparedMessage" />
              <type value="JsonLogs.CustomLayoutConverter" />
            </converter>
            <default />
            <remove value='message' />
            <remove value='ndc' />
            <member value='message:messageObject' />
            <member value='details:preparedMessage' />
        </layout> 
    </appender>
    
    <appender name="Console2" type="log4net.Appender.ConsoleAppender">
        <layout type="log4net.Layout.PatternLayout">
            <converter>
              <name value="preparedMessage" />
              <type value="JsonLogs.CustomLayoutConverter" />
            </converter>
            <conversionPattern value="%level %thread %logger - %preparedMessage%newline" />
        </layout>
    </appender>
    
    <root>
        <level value="DEBUG" />
        <appender-ref ref="Console" />
        <appender-ref ref="Console2" />
    </root>
</log4net>

with the following implementation of mu custom PatternLayoutConverter:

namespace JsonLogs
{
    using System.IO;

    using log4net.Core;
    using log4net.Layout.Pattern;

    public class CustomLayoutConverter : PatternLayoutConverter
    {
        #region Methods

        protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
        {
            if (loggingEvent.MessageObject is string stringMessage)
            {
                writer.Write(new { message = stringMessage });
            }
            else
            {
                writer.Write(loggingEvent.RenderedMessage);
            }
        }

        #endregion
    }
}

For some reason, the converter works perfectly fine with the Console2 appender(which is not JSON driven) but it doesn't work with the Console appender whose output is JSON. Example of the output:

Console -> {"date":"2018-12-09T12:25:28.0529041+03:00","level":"INFO","appname":"JsonLogs.exe","logger":"JsonLogs.Program","thread":"1","message":"Test","details":"preparedMessage"}
Console2 -> INFO 1 JsonLogs.Program - { message = Test }

My goal is to have details always in JSON that's why I introduced my converter to catch primitive values and wrap them in a custom object.

Is my configuration wrong? Or I'm missing something? Could you help me, please, to figure this out?

Thank you

Edited by Jo Cutajar
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information