应直接在InstanceData的InstanceName中嵌入唯一标记(如GUID或TraceId),利用InstanceDataCollection的上下文隔离性,配合TaggedSample包装类或OpenTelemetry Activity传播实现端到端标记对齐。

直接在每个性能计数器实例中绑定唯一标记,关键不是靠全局变量或手动命名,而是利用 InstanceData 的天然结构和 RawValue 的上下文关联性,让标记随采样生命周期自动存活。
用 InstanceDataCollection 包裹带标识的采样数据
每个 PerformanceCounter 实例可对应一个独立的 InstanceDataCollection,它本身不存储标记,但你可以将标记作为元数据注入其子项。实际做法是:创建 InstanceData 对象时,把自定义 ID(如 GUID、线程ID、请求TraceId)赋给它的 InstanceName 属性,而非仅用系统默认名。
-
不要依赖
CounterName或CategoryName做区分——它们是共享的 -
要在构造
InstanceData时显式设置InstanceName = $"req_{Guid.NewGuid()}"或$"thread_{Thread.CurrentThread.ManagedThreadId}" - 后续调用
NextSample()得到的CounterSample虽无直接标记字段,但其RawValue值始终与该InstanceData所属实例上下文绑定
在采样逻辑中嵌入标记快照
由于 RawValue 是只读的 long 类型原始值,无法直接存字符串标记,但你可以在触发采样的瞬间,用字典或 ConcurrentDictionary 将当前 CounterSample 的 RawValue 与标记做一次映射。重点是“采样发生时”这个时间点必须精准。
- 每次调用
performanceCounter.NextSample()后,立即执行:sampleMap.TryAdd(sample.RawValue, currentTag) - 为避免
RawValue冲突(极小概率),建议组合使用RawValue + Environment.TickCount64作为键 - 若需长期追踪,把标记写入
CounterSample.SystemFrequency或CounterSample.TimeStamp的低32位(需位运算掩码,仅限调试场景)
通过 CounterSample 构造函数注入上下文线索
.NET 并未开放 CounterSample 的 public 构造函数供外部传参,但你可以封装一个轻量包装类,在采集阶段就完成绑定:
- 定义
TaggedSample { public CounterSample Sample; public string Tag; public DateTime CaptureTime; } - 所有采样入口统一走
GetTaggedSample("api_login_v2"),内部调用NextSample()并立即打包 - 后续计算(如
PerformanceCounter.Calculate())仍用原生CounterSample,但分析报表时可从TaggedSample中提取上下文
配合实时指标做端到端标记对齐
如果目标是和 Azure Application Insights 的实时指标联动,推荐跳过底层 RawValue 操作,改用 OpenTelemetry 的 Activity 上下文传播机制。此时性能采样标记应来自 Activity.Current?.Id 或 Activity.Current?.TagObjects,再通过自定义 TelemetryProcessor 注入到性能计数器维度中。
- 在 ASP.NET Core 中间件里开启
ActivitySource,为每个请求生成唯一 Activity - 在性能采集点调用
Activity.Current?.AddTag("perf.scope", "db_query") - Application Insights 会自动将这些 tag 同步到实时指标流,无需手动维护
RawValue映射


















