Lazy evaluation with buffers in AdoNetAppender in Log4Net

混江龙づ霸主 提交于 2019-12-11 02:57:34

问题


I'm using Log4Net custom properties to add some environmental information into my logs. I created an utility class with global accessible properties that my program classes use to store context information (order id, user id, etc) and a lazy wrapper around them so I don't need to change Log4Net ThreadContext all the time. Something like this:

public class LoggerPropertyProvider
{
    private readonly string _value;

    public LoggerPropertyProvider(string value)
    {
        _value = value;
    }

    public override string ToString()
    {
        return _value;
    }
}

Whatever value I want to expose as a property to Log4Net I just register using this lazy evaluator at the start of the application.

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

It works just fine with buffer-less appenders (such as rolling file) or when the buffer is set to 0 in AdoNetAppender. However, when I have buffer > 1 Log4Net defers the evaluation of the property until the buffer is flushed at the end of the application or when entries in buffer > bufferSize.

When this happens the information is not in the global property anymore or it's value has been changed (like a loop processing orders) so I get a wrong or null value in my logs.

The only option I can see to fix this is stop using buffers so the property value is evaluated in all calls when the entry is being flushed. Since the properties in ThreadContext are only evaluated when the buffer is being flushed I'm afraid I cannot have a different property value for each log call.

Is there any way I can make Log4Net evaluate a ThreadContext (or some other context it has) when buffering the entry instead of when it flushes it?

Thanks


回答1:


By default, log4net does not fix property provider values from contexts at log time for buffered appenders. They get evaluated at buffer flush time.

To solve this issue, you have to implement IFixingRequired from log4net.Core namespace.

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();
    }
}

(Tested with my own property provider, which relies on http context due to my own needs:

// 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();
    }
}

General idea coming from piers blog. See this my other answer to another question if you'd like more up to date details about this HttpContextValueProvider.)




回答2:


It seems you do have to put the property in the log4net thread context:

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

This context overrides any properties in the global context and is managed by log4net it self:

log4net.ThreadContext



来源:https://stackoverflow.com/questions/26587190/lazy-evaluation-with-buffers-in-adonetappender-in-log4net

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!