如何在NLog AsyncTaskTarget中获取ScopeContext的作用域属性值
问题根因
直接在AsyncTaskTarget的写入方法里调用静态的ScopeContext、MappedDiagnosticsLogicalContext接口拿不到值,是因为AsyncTaskTarget默认走异步队列消费逻辑:触发日志的线程只会把LogEventInfo对象塞进内部队列就返回,实际写入操作是由后台独立的线程池线程完成的。后台线程已经脱离了当初调用BeginScope的异步执行上下文,存在于逻辑调用上下文里的作用域数据自然读不到。
至于${mdlc:userId}能正常渲染,是因为NLog在日志入队前、原始上下文还没丢失的阶段,就已经把所有作用域的属性提前捕获,挂到了LogEventInfo的Properties集合上。布局渲染是直接读LogEventInfo上存好的值,根本不需要访问当前线程的上下文,所以不受线程切换的影响。
推荐取值方案
直接从写入方法传入的LogEventInfo实例的Properties字典里取对应键即可,这也是性能最优、NLog官方推荐的方式:
protected override async Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token) { // 从预捕获的属性集合读取作用域值 if (logEvent.Properties.TryGetValue("userId", out var userId)) { // 这里处理拿到userId后的业务逻辑 } var message = this.Layout.Render(logEvent); }
非必要不使用的替代方案
如果你有特殊需求,一定要在写入方法里通过静态ScopeContext接口取值,可以在自定义Target的构造函数里关闭默认的上下文拷贝优化,强制写入时保留原始执行上下文:
public CustomLogTarget() { // 关闭上下文拷贝优化,会带来额外的异步性能损耗 this.ScopeContextCopyOnWrite = false; }
改完这个配置后,ScopeContext.TryGetProperty就能正常返回值,但这个方案会增加异步上下文跨线程拷贝的开销,日志量大的时候性能影响很明显,没有特殊需求不要用。
内容的提问来源于stack exchange,提问作者marsze
相关产品推荐
相关产品推荐

