You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 04:17:04