如何将NLog等日志工具集成到Dynamics CRM 2011的ITracingService?
针对Dynamics CRM 2011插件/自定义操作的NLog集成方案
1. 构建对接ITracingService的NLog自定义Target
由于ITracingService是在插件/自定义操作的执行上下文内才能获取的实例,无法直接在NLog Target初始化时注入,我们可以通过AsyncLocal实现上下文级别的实例传递,让自定义Target能拿到当前执行上下文的ITracingService。
步骤1:定义上下文存储类
用于在当前执行线程中存储和获取ITracingService实例:
public static class CrmTracingContext { private static readonly AsyncLocal<ITracingService> _tracingService = new AsyncLocal<ITracingService>(); public static ITracingService Current { get => _tracingService.Value; set => _tracingService.Value = value; } }
步骤2:实现自定义NLog Target
继承NLog的TargetWithLayout,重写Write方法,将日志消息通过ITracingService.Trace输出:
[Target("CrmTracing")] public class CrmTracingTarget : TargetWithLayout { protected override void Write(LogEventInfo logEvent) { var tracingService = CrmTracingContext.Current; if (tracingService == null) return; // 用NLog的Layout渲染格式化后的日志消息 var formattedMessage = Layout.Render(logEvent); tracingService.Trace(formattedMessage); } }
步骤3:配置NLog
在NLog.config中注册自定义Target(需将配置文件设置为嵌入资源,随插件程序集部署):
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <extensions> <!-- 替换为你的插件程序集名称 --> <add assembly="Your.Plugin.Assembly"/> </extensions> <targets> <target xsi:type="CrmTracing" name="crmTrace" layout="${longdate} ${level:uppercase=true} ${message} ${exception:format=ToString}"/> </targets> <rules> <logger name="*" minlevel="Trace" writeTo="crmTrace"/> </rules> </nlog>
2. 线程安全的日志工具实例化方案
NLog官方推荐将Logger声明为静态变量,这完全适配Dynamics CRM插件/自定义操作的无状态要求:
Logger本身是无状态的,不会存储任何执行上下文相关数据- 我们通过
AsyncLocal实现的上下文存储,会自动隔离不同线程(包括线程池复用的线程)的ITracingService实例,避免上下文混淆
插件中使用示例
public class SamplePlugin : IPlugin { // 静态Logger,符合NLog最佳实践 private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); public void Execute(IServiceProvider serviceProvider) { var tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); try { // 将当前上下文的ITracingService存入AsyncLocal CrmTracingContext.Current = tracingService; // 正常使用NLog的多级别日志方法 _logger.Trace("插件执行启动"); _logger.Info("处理实体ID: {EntityId}", Guid.NewGuid()); // ... 业务逻辑代码 } catch (Exception ex) { _logger.Error(ex, "插件执行异常"); throw new InvalidPluginExecutionException("操作失败", ex); } finally { // 清理上下文,防止线程池复用时残留旧实例 CrmTracingContext.Current = null; } } }
自定义操作中使用示例
public class SampleCodeActivity : CodeActivity { private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); protected override void Execute(CodeActivityContext context) { var tracingService = context.TracingService; try { CrmTracingContext.Current = tracingService; _logger.Debug("自定义操作执行中"); // ... 业务逻辑代码 } catch (Exception ex) { _logger.Fatal(ex, "自定义操作执行失败"); throw; } finally { CrmTracingContext.Current = null; } } }
关键注意事项
- 必须在
finally块中清理CrmTracingContext.Current,避免线程池线程复用时携带旧的上下文数据 - 可根据需求调整NLog的
Layout配置,添加调用栈、日志来源等字段,丰富Trace输出内容 - 确保NLog配置文件被正确嵌入到插件程序集中,否则自定义Target无法被NLog识别
内容的提问来源于stack exchange,提问作者Varthorne
相关产品推荐
相关产品推荐

