log4net 复杂类型日志记录

log4net complex type logging

我想在 C# 中记录复杂类型的对象并使用 log4net 进行记录。执行此操作的正确方法是什么?我使用 filelogappender.So 我想在 log.Should 中查看我的实体 我这样做是为了使用 stringbuilder class 并将我的实体转换为字符串或将其序列化为 json 还是 log4net 有这个能力?

public class Person
{
Name {get;set;}
Surname{get;set;}
}

Person personobject=new Person("MyName","MySurname");
log4net.Log.Warn(personobject);

如果您想将复杂的对象记录到文件或 sql,我赞同第一条评论,最好的方法是使用 newtonsoft 库(nuget 包)通过 json 转换字符串)

然后您可以随意包装和打开对象或动态更改数据(json 将为您节省大量时间和精力。)您还可以将其传回前端以进行控制台日志记录或调试并查看您刚刚记录的对象。

不能记录复杂类型,原因很简单:

日志机制执行了一项足够艰巨的工作 - 每次记录信息时都必须打开流并关闭流

您流式传输的数据主要是文本,没有简单的方法将复杂对象解析为文本表示,因此您看到的是 'shallow' 类型的文本表示:'Object object'

简单的解决方法是手动将对象序列化为 JSON 字符串,这样:

log4net.Log.Warn(Newtonsoft.Json.JsonConvert.SerializeObject(personobject));

这会将您的类型序列化为字符串,而不是对象,因此您将能够记录您想要的任何复杂类型。

与@micah 一样,我建议 JSON,这次使用现有解决方案:log4net.Ext.Json。安装包后,您只需将 SerializedLayout 添加到任何附加程序即可:

<appender...>
    <layout type='log4net.Layout.SerializedLayout, log4net.Ext.Json'>
    </layout> 
</appender>

您可以创建格式化日志消息的自定义布局:

结构布局

public class StructLayout : PatternLayout
{
    public override void Format(TextWriter writer, LoggingEvent loggingEvent)
    {
        LoggingEvent newLoggingEvent;
        var message = loggingEvent.MessageObject;

        if (MessageObjectBuilder.TryBuildMessageObject(message, out object newMessage))
        {
            var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            var callerStackBoundaryDeclaringType = loggingEvent.GetType().GetField("m_callerStackBoundaryDeclaringType", bindingFlags)?.GetValue(loggingEvent);
            newLoggingEvent =
                new LoggingEvent(callerStackBoundaryDeclaringType as Type,
                    loggingEvent.Repository,
                    loggingEvent.LoggerName,
                    loggingEvent.Level,
                    newMessage as SystemStringFormat,
                    loggingEvent.ExceptionObject);
        }
        else
        {
            newLoggingEvent = loggingEvent;
        }

        base.Format(writer, newLoggingEvent);
    }
}

MessageObjectBuilder - 使用 Newtonsoft 格式化消息:

 public class MessageObjectBuilder
{
    public static bool TryBuildMessageObject(object messageObject, out object newMessage)
    {
        var message = messageObject;
        var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        object[] args = message.GetType().GetField("m_args", bindingFlags)?.GetValue(message) as object[];

        if (args == null || args.Length == 0) // not a formatted message 
        {
            newMessage = message;
            return false;
        }

        var argsAsJson = SerializeArgs(args);
        var provider = message.GetType().GetField("m_provider", bindingFlags)?.GetValue(message);
        var format = message.GetType().GetField("m_format", bindingFlags)?.GetValue(message);

        newMessage = new SystemStringFormat(provider as IFormatProvider, format as string, argsAsJson);
        return true;
    }

    private static object[] SerializeArgs(object[] args)
    {
        var newArgs = new List<string>();
        foreach (var arg in args)
        {
            newArgs.Add(Newtonsoft.Json.JsonConvert.SerializeObject(arg));
        }

        return newArgs.ToArray();
    }
}

最后将此添加到 log4net.config 而不是您当前的布局:

    <appender name="StructLogAppender" type="log4net.Appender.RollingFileAppender">
        <file value="StructLogAppender\StrcutLog.log" />
        <layout type="Namespace.StructLayout, AssmbllyName">
            <conversionPattern value="%date{M/d/yyyy H:mm:ss.fff} - %message%newline" />
        </layout>
        <filter type="log4net.Filter.LevelRangeFilter">
            <param name="LevelMax" value="ERROR" />
        </filter>
        <appendToFile value="true" />
   </appender>

Note:

  • 这段代码使用了反射,所以它可能有点重,所以它不是 如果性能有问题,推荐使用。
  • 确保在 log4net.config
  • 中自定义 NamespaceAssmbllyName