所以我们有一个日志系统,我们使用Log.Info
并写入ILogger
。
现在我们有多个工作人员在后台运行,我们希望这些工作人员写入他们自己的日志。所以每个工人都捆绑在一起。执行此任务时记录的所有内容都应转发给它自己的记录器。
我们考虑制作一个方法Log.SetLoggerForCurrentThread,用ThreadLocal实现它。执行代码看起来像这样:
public class Worker
{
ILogger _Logger;
public void ExecuteTask()
{
Log.Info( "This goes to the 'Global' logger" );
using ( Log.SetLoggerForCurrentThread(_Logger) )
{
Log.Info( "This goes to local logger" );
DoWork();
}
}
private async void DoWork()
{
Log.Info( "Starting..." );
// SomeMethod does some logging,
// that also needs to be forwared to the local logger
var value = await SomeDeepDomainClass.SomeMethod();
// if we use ThreadLocal, and this thread has been reused,
// it could be a completely different logger that is now attached.
Log.Info( "Ended..." );
}
}
问题
CultureInfo
如何处理此问题?背景资料
这些Workers中的大多数将在Azure WorkerRole实例中运行,但现在和它们也是从控制台应用程序触发(一次)。
答案 0 :(得分:6)
您可以使用CallContext跨线程传递(可序列化)数据。请参阅此文章以获取示例:
http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html
有关背景信息,请参阅此文:
http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx
答案 1 :(得分:6)
在我看来,最好的解决方案是将记录器实例作为参数(或成员变量)传递,或者注入它们(例如,使用嵌套的范围)。
但是,如果要以与await
兼容的方式隐式存储和传递日志记录实例,则需要使用逻辑调用上下文。我有一个blog post describing this approach,它指出了这种方法的局限性:
考虑到这一点,这里有一些代码可以满足您的需求:
public static class LocalLogger
{
private static readonly string name = Guid.NewGuid().ToString("N");
// Static Log methods should read this.
public static ILogger CurrentLogger
{
public get
{
var ret = CallContext.LogicalGetData(name) as ILogger;
return ret == null ? Logger.GlobalLogger : ret;
}
private set
{
CallContext.LogicalSetData(name, value);
}
}
// Client code uses this.
public static IDisposable UseLogger(ILogger logger)
{
var oldLogger = CurrentLogger;
CurrentLogger = logger;
if (oldLogger == GlobalLogger)
return NoopDisposable.Instance;
return new SetWhenDisposed(oldLogger);
}
private sealed class NoopDisposable : IDisposable
{
public void Dispose() { }
public static readonly Instance = new NoopDisposable();
}
private sealed class SetWhenDisposed : IDisposable
{
private readonly ILogger _oldLogger;
private bool _disposed;
public SetWhenDisposed(ILogger oldLogger)
{
_oldLogger = oldLogger;
}
public void Dispose()
{
if (_disposed)
return;
CurrentLogger = _oldLogger;
_disposed = true;
}
}
}