使用 log4net.Ext.Json 自定义 PatternLayoutConverter?

Custom PatternLayoutConverter with log4net.Ext.Json?

我有以下 log4net 配置:

<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>

我的自定义 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
    }
}

出于某种原因,转换器与 Console2 appender(不是 JSON 驱动的)一起工作得很好,但它不适用于输出为 JSON 的 Console appender。 输出示例:

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 }

我的目标是让 details 始终在 JSON 中,这就是为什么我引入了自己的转换器来捕获原始值并将它们包装在自定义对象中的原因。

我的配置有问题吗?或者我错过了什么?你能帮我解决这个问题吗?

谢谢

这个问题似乎是 log4net.Ext.Json 的错误。我将在他们的 GitLab 上报告它。 到目前为止,我最终得到了我的自定义 log4net 布局,它看起来像这样

public class CustomLayout : PatternLayout
{
    #region Public Methods and Operators

    public override void Format(TextWriter writer, LoggingEvent loggingEvent)
    {
        var message = loggingEvent.MessageObject.GetType().IsPrimitive || loggingEvent.MessageObject is string || loggingEvent.MessageObject is decimal || loggingEvent.MessageObject is BigInteger
            ? new { message = loggingEvent.MessageObject }
            : loggingEvent.MessageObject;

        writer.WriteLine(JsonConvert.SerializeObject(new
        {
            timestamp = loggingEvent.TimeStampUtc,
            threadId = loggingEvent.ThreadName,
            details = message,
            logger = loggingEvent.LoggerName,
            level = loggingEvent.Level.DisplayName,
            user = loggingEvent.UserName
        }));
    }

    #endregion
}

它满足了我的需求并且完全符合我的要求。

这个问题的确切出处是AddMember方法及其实现。这是 SerializedLayout 的源代码:

        public virtual void AddMember(string value)
        {
            var arrangement = log4net.Util.TypeConverters.ArrangementConverter.GetArrangement(value, new ConverterInfo[0]);
            m_arrangement.AddArrangement(arrangement);
        }

正如您所见,GetArrangment 的第二个参数是 ConverterInfo 的空数组,但必须有我们自定义的附加参数(通过 AddConverter 方法或通过 xml)。 作为解决方案,您可以实现自己的子类,该子类将派生自 SerializedLayout 并重写 AddMember,如下所示:

    public override void AddMember(string value)
    {
        var customConverter = new ConverterInfo("lookup", typeof(CustomPatternConverter));
        var arrangement = log4net.Util.TypeConverters.ArrangementConverter.GetArrangement(value, new ConverterInfo[] { customConverter });
        m_arrangement.AddArrangement(arrangement);
    }

希望它对我的案例有所帮助!