.NET 8中跨边界启动/延续Activity的正确方式及Aspire追踪异常排查
我运行着多个微服务处理大量工作负载,正尝试通过.NET Aspire追踪特定工作负载的流转路径,当前包含3个后台Worker项目与1个Web API项目。
第一个Worker在工作负载到达时启动Activity,完成后将其推入RabbitMQ队列,并将当前Activity的Id放入消息头;Worker 2处理后采用相同方式将消息推送给Worker 3,代码如下:
basicProperties.Headers = new Dictionary<String, Object>(); if (Activity.Current?.Id != null) { basicProperties.Headers.Add(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName, Activity.Current?.Id); }
Worker 2和3通过以下代码启动Activity:
if (basicProperties.Headers?.TryGetValue(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName, out var parentActivityIdRaw) == true && parentActivityIdRaw is Byte[] traceParentBytes) { parentActivityId = Encoding.UTF8.GetString(traceParentBytes); } return activitySource.StartActivity("Incoming message from queue", kind: ActivityKind.Consumer, parentId: parentActivityId);
最后Worker 3通过HttpClient调用Web API。但Aspire追踪页面仅显示Worker1、Worker3与Web API,未显示Worker2;且Worker3中自定义添加的事件未在详情页展示,仅显示HttpClient的事件。
我想了解跨边界延续Activity的正确方式,为何HttpClient能正常延续并上报事件,而自定义使用Activity.Current添加的事件无法在追踪详情中显示?
一、Worker2未出现在追踪中的原因及修复
Activity未绑定到当前上下文
你当前代码仅启动了Activity但未将其设置为Activity.Current,导致Worker2的业务逻辑无法关联到该Activity。必须用using块自动管理Activity的上下文生命周期,确保整个处理流程都关联到这个Activity:using var activity = activitySource.StartActivity("Incoming message from queue", kind: ActivityKind.Consumer, parentId: parentActivityId); if (activity == null) return; // 追踪被禁用时StartActivity会返回null // 在这里执行Worker2的业务逻辑using块会自动将启动的Activity设为当前上下文,结束时恢复之前的状态,确保Aspire能采集到Worker2的追踪数据。缺少生产者Activity关联链路
Worker2推送消息时,需要启动ActivityKind.Producer类型的子Activity,关联到当前的消费Activity,才能形成Worker2到Worker3的完整链路。你当前仅传递了父Activity ID,未创建生产者Activity,导致链路断裂:using var producerActivity = activitySource.StartActivity("Send message to queue", kind: ActivityKind.Producer); if (producerActivity != null) { basicProperties.Headers = new Dictionary<string, object>(); basicProperties.Headers.Add(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName, producerActivity.Id); }生产者Activity会自动关联到Worker2的消费Activity,形成父子关系,Aspire就能识别到Worker2的存在。
二、Worker3自定义事件未显示的原因及修复
事件未关联到正确的Activity实例
自定义事件必须调用当前Activity实例的AddEvent方法,而非仅依赖Activity.Current。正确做法是在启动Activity后直接操作该实例:using var activity = activitySource.StartActivity("Incoming message from queue", kind: ActivityKind.Consumer, parentId: parentActivityId); if (activity == null) return; // 添加自定义事件 activity.AddEvent(new ActivityEvent("Custom Worker3 Event", DateTimeOffset.UtcNow)); // 执行后续业务逻辑(包括HttpClient调用)HttpClient的Handler会自动获取
Activity.Current并创建子Activity,所以能正常显示;如果自定义事件未绑定到当前Activity实例,Aspire无法采集到这些事件。确认ActivitySource被Aspire追踪
检查Worker3的ActivitySource名称是否在Aspire的追踪配置中被包含,默认配置可能会排除部分来源。可在appsettings.json中明确配置:"Aspire": { "Telemetry": { "Tracing": { "IncludeActivitySources": [ "Your.Worker3.ActivitySource.Name" ] } } }
三、跨边界延续Activity的标准流程
生产者端
- 启动
ActivityKind.Producer类型的Activity,自动关联到当前上下文 - 将该Activity的完整ID放入消息头(作为traceparent)
- 用
using块管理Activity生命周期,确保消息发送完成后正确结束
- 启动
消费者端
- 从消息头取出traceparent值
- 启动
ActivityKind.Consumer类型的Activity,指定parentId为取出的traceparent - 用
using块绑定Activity到当前上下文,覆盖整个消费处理流程 - 若需转发消息,重复生产者端的流程
内容的提问来源于stack exchange,提问作者Steen Tøttrup

