Log4Net中AdoNetAppender中缓冲区的懒惰评估

本文关键字:评估 缓冲区 AdoNetAppender Log4Net | 更新日期: 2023-09-27 18:27:54

我正在使用Log4Net自定义属性向日志中添加一些环境信息。我创建了一个具有全局可访问属性的实用程序类,我的程序类使用这些属性来存储上下文信息(订单id、用户id等),并在它们周围使用惰性包装器,这样我就不需要一直更改Log4Net ThreadContext。类似这样的东西:

public class LoggerPropertyProvider
{
    private readonly string _value;
    public LoggerPropertyProvider(string value)
    {
        _value = value;
    }
    public override string ToString()
    {
        return _value;
    }
}

无论我想将什么值作为属性公开给Log4Net,我都只需在应用程序开始时使用惰性计算器进行注册。

ThreadContext.Properties["ORDER_ID"] = new LoggerPropertyProvider(ContextInformation.OrderId);

它与无缓冲区的附加程序(如滚动文件)或AdoNetAppender中缓冲区设置为0时工作得很好。但是,当我有buffer>1Log4Net时,会推迟对属性的评估,直到应用程序结束时刷新缓冲区,或者当buffer>bufferSize中的条目时。

当这种情况发生时,信息不再在全局属性中,或者它的值已经更改(就像处理订单的循环),所以我在日志中得到了一个错误的或null的值。

我能看到的唯一解决这个问题的选项是停止使用缓冲区,这样在刷新条目时,所有调用都会计算属性值。由于ThreadContext中的属性只有在刷新缓冲区时才会求值,所以恐怕我不能为每个日志调用指定不同的属性值。

有没有什么方法可以让Log4Net在缓冲条目时而不是在刷新条目时评估ThreadContext(或它拥有的其他上下文)?

感谢

Log4Net中AdoNetAppender中缓冲区的懒惰评估

默认情况下,log4net不会在日志时间为缓冲的appenders从上下文中fix属性提供程序值。它们在缓冲区刷新时间进行评估。

为了解决这个问题,您必须从log4net.Core命名空间实现IFixingRequired。

public class LoggerPropertyProvider : IFixingRequired
{
    private readonly string _value;
    public LoggerPropertyProvider(string value)
    {
        _value = value;
    }
    public override string ToString()
    {
        return _value;
    }
    object IFixingRequired.GetFixedObject()
    {
        return ToString();
    }
}

(使用我自己的属性提供商进行了测试,由于我自己的需要,该提供商依赖于http上下文:

// We can not use log4net ThreadContext or LogicalThreadContext with asp.net, since
// asp.net may switch thread while serving a request, and reset the call context in
// the process.
public class HttpContextValueProvider : IFixingRequired
{
    private string _contextKey;
    public HttpContextValueProvider(string contextKey)
    {
        _contextKey = contextKey;
    }
    public override string ToString()
    {
        var currContext = HttpContext.Current;
        if (currContext == null)
            return null;
        var value = currContext.Items[_contextKey];
        if (value == null)
            return null;
        return value.ToString();
    }
    object IFixingRequired.GetFixedObject()
    {
        return ToString();
    }
}

总的想法来自皮尔斯博客。看看这是我对另一个问题的另一个回答如果您想了解有关此HttpContextValueProvider的更多最新详细信息。)

似乎必须将属性放在log4net线程上下文中:

log4net.ThreadContext.Properties["ORDER_ID"] = new LoggerPropertyProvider(ContextInformation.OrderId);

此上下文覆盖全局上下文中的任何属性,并由log4net-it-self:管理

log4net.ThreadContext