2

我正在使用 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 时。但是,当我有缓冲区 > 1时, Log4Net 会推迟对该属性的评估,直到缓冲区在应用程序结束时刷新或缓冲区中的条目 > bufferSize时。

发生这种情况时,信息不再位于全局属性中,或者它的值已更改(如循环处理订单),因此我的日志中出现错误或空值。

我可以看到解决此问题的唯一选项是停止使用缓冲区,以便在刷新条目时在所有调用中评估属性值。由于仅在刷新缓冲区时才评估 ThreadContext 中的属性,因此恐怕我不能为每个日志调用设置不同的属性值。

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

谢谢

4

2 回答 2

4

默认情况下,log4net 不会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。)

于 2015-08-31T09:42:08.923 回答
0

看来您确实必须将该属性放在 log4net 线程上下文中:

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

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

log4net.ThreadContext

于 2014-10-28T07:48:27.743 回答