Some years ago I wrote a couple of posts on some nasty problems that you could encounter if using log4net contexts in an environment where you didn’t control the thread lifecycle, say ASP.Net. Judging by the amount of coverage it got at the time (and still) I wasn’t the only person caught out by this.
Anyway I was doing something similar recently, not in ASP.Net, but in a Windows Service application with lots of threads. It’s the same kind of problem: there’s some thread-specific context that always exists, which we want to make available to log4net, but putting it in ThreadLocalContext doesn’t really work very well because we’d have to set them up in all our thread-entry methods, which would be everywhere where a callback gets entered – very messy in our (highly asynchronous) application.
Instead I wanted to put something in log4net’s GlobalContext that resolved to the thread’s context value. And actually now we’ve got lamdas and all that nice stuff, I was able to come up with a significantly neater implementation for a general-purpose contextual logging property, which basically answers the original ASP.Net problem too:
/// <summary>
/// Implements a class that can be used as a global log4net property
/// to resolve an action to a string at event-fixing-time
/// </summary>
/// <remarks>With a suitable lamda expression, you can put this
/// into your log4net.GlobalContext to resolve at logging time to a variety
/// of stuff you might want to use in your logging statements.
/// <example>Using threadId (not thread Name) as a property:<code>
/// log4net.GlobalContext.Properties["threadId"] =
/// new Log4NetContextProperty(() => Thread.CurrentThread.ManagedThreadId.ToString());
/// </code></example>
/// </remarks>
public class Log4NetContextProperty : IFixingRequired
{
private readonly Func<string> _getValue;
public Log4NetContextProperty(Func<string> getValue)
{
_getValue = getValue;
}
public override string ToString()
{
return _getValue();
}
public object GetFixedObject()
{
return ToString();
}
}
In this case I wanted ‘threadId’ as a logging property (log4net exposes thread name, which is normally fine, but the R# test runner creates woppingly long thread names that basically hide the actual logging message, and I really just wanted the IDs (hence the example above). But you can see how you can basically use this to expose any context data to log4net if you wanted to.