- 示例工程
【免费下载链接】practical-aspnetcore
Practical samples of ASP.NET Core 11, 10, 9, 8.0, 7.0, 6.0, 5.0, 3.1, 2.2, and 2.1,projects you can use. Readme contains explanations on all projects.
本指南基于 practical-aspnetcore 仓库中的 open-telemetry-4 示例,讲解如何在 ASP.NET Core 应用中记录 OpenTelemetry(OTel)Baggage,并借助 HttpClient 探针自动以baggageHTTP 头把用户自定义属性传给下游 HTTP 请求。读完本文,你将掌握Activity.AddBaggage的写入方式、Baggage 与 Span Attribute 的区别、baggage头的自动注入与手动注入两种路径,以及如何在接收端读取并验证传递结果。
一、什么是 OTel Baggage
在 OpenTelemetry 中,Baggage是随请求一起在服务之间传播的一组键值对(key/value pairs),由 W3CBaggage 规范定义。与 Trace 中的 Span 不同:
- Span Attribute(属性):只属于当前 Span,记录在当前服务的某个 trace 段上,不会随请求传递给下游服务(见仓库中的 open-telemetry-3 示例,它演示了如何用
SetTag记录project、location属性)。 - Baggage:绑定在请求上下文中,可以跨越进程边界,通过标准的
baggageHTTP 请求头传递给下一个服务。
典型使用场景包括:把用户 ID、租户 ID、请求来源、A/B 实验分组等"业务上下文"从入口服务一路带到链路下游,供各服务做路由、限流或观测过滤,而无需在每个服务里重复解析业务参数。
在 .NET 的System.Diagnostics.Activity模型中,当前请求的上下文就是Activity.Current,我们可以直接在它上面附加 Baggage。
二、项目结构与依赖配置
示例位于仓库 projects/open-telemetry/open-telemetry-4 目录,只有三个文件:
open-telemetry-4/ ├── Program.cs # 全部逻辑 ├── Readme.md # 示例说明 └── open-telemetry-4.csproj # 项目文件open-telemetry-4.csproj 目标框架为net10.0(仓库根目录的 global.json 将 SDK 锁定为 10.0.0 并允许rollForward: major),引用的 OTel 相关包均为1.17.0:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <ImplicitUsings>true</ImplicitUsings> </PropertyGroup> <ItemGroup> <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.17.0" /> <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.17.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.17.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.http" Version="1.17.0" /> </ItemGroup> </Project>与仓库中前三个 OTel 示例(open-telemetry-1、open-telemetry-2、open-telemetry-3)相比,本示例的关键差异是引入了OpenTelemetry.Instrumentation.Http(HTTP 客户端探针)——正是它负责在出站请求上自动注入baggage头。注意 NuGet 包名大小写不敏感,源码中写成的http与官方包名Http等价。
三、核心实现拆解
Program.cs 全部代码如下:
using System.Diagnostics; using OpenTelemetry.Trace; using OpenTelemetry.Resources; var builder = WebApplication.CreateBuilder(); builder.Services.AddOpenTelemetry().WithTracing(b => { b.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(builder.Environment.ApplicationName)) .AddHttpClientInstrumentation(); }); builder.Services.AddHttpClient(); builder.Logging.SetMinimumLevel(LogLevel.Information); WebApplication app = builder.Build(); app.MapGet("/", async (HttpRequest request, IHttpClientFactory clientFactory) => { Activity.Current?.AddBaggage ("project", "practical-aspnetcore"); Activity.Current?.AddBaggage ("location", "Cairo"); var baggage = string.Join(",", Activity.Current.Baggage.ToDictionary(b => b.Key, b => b.Value).Select(x => x.Key + "=" + x.Value)); var client = clientFactory.CreateClient(); //client.DefaultRequestHeaders.Add("baggage", baggage); var url = request.Scheme + "://" + request.Host + "/baggage"; app.Logger.LogInformation("REQUEST URL " + url); var response = await client.GetAsync(url); return Results.Text("Check the log", "text/plain"); }); app.MapGet("/baggage", (HttpRequest request) => { var baggage = request.Headers["baggage"]; app.Logger.LogInformation($"BAGGAGE VALUES {baggage}"); return Results.Ok(); }); app.Run();3.1 启动阶段:注册 OpenTelemetry 与 HTTP 客户端
builder.Services.AddOpenTelemetry().WithTracing(b => { b.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(builder.Environment.ApplicationName)) .AddHttpClientInstrumentation(); }); builder.Services.AddHttpClient();ResourceBuilder.CreateDefault().AddService(builder.Environment.ApplicationName):给 trace 资源打上service.name(值为当前应用名),与 open-telemetry-1 的用法一致。AddHttpClientInstrumentation():启用 HTTP 客户端探针,监听出站请求并完成上下文(含 Baggage)注入。builder.Services.AddHttpClient():注册IHttpClientFactory,后面通过工厂创建HttpClient。builder.Logging.SetMinimumLevel(LogLevel.Information):把日志级别调到 Information,确保关键日志能在控制台看到(本示例没有配置AddConsoleExporter,因此观察点全部在应用日志上)。
3.2 写入 Baggage:AddBaggage
在根路由处理函数中,通过两行代码把用户自定义属性写入当前 Activity 的 Baggage:
Activity.Current?.AddBaggage ("project", "practical-aspnetcore"); Activity.Current?.AddBaggage ("location", "Cairo");这里Activity.Current是当前 HTTP 请求对应的 Activity(ASP.NET Core 内建的Microsoft.AspNetCoreActivitySource 提供;从 .NET 11 起该 Source 会原生填充 OTel 语义属性,早期版本则依赖OpenTelemetry.Instrumentation.AspNetCore补充,参见 net11/native-otel-tracing 说明)。AddBaggage(key, value)以键值对形式把业务上下文挂到当前请求上下文上。
紧随其后的这行代码展示了如何把 Baggage 序列化成标准的key=value列表:
var baggage = string.Join(",", Activity.Current.Baggage.ToDictionary(b => b.Key, b => b.Value).Select(x => x.Key + "=" + x.Value));它先把Activity.Current.Baggage(IEnumerable<KeyValuePair<string, string>>)转成字典,再拼接为project=practical-aspnetcore,location=Cairo这样的 W3C Baggage 字符串——这正是手动注入方案(下一节)需要的头值。
3.3 自动注入与手动注入两条路径
示例中真正发起下游请求的代码是:
var client = clientFactory.CreateClient(); //client.DefaultRequestHeaders.Add("baggage", baggage); var url = request.Scheme + "://" + request.Host + "/baggage"; app.Logger.LogInformation("REQUEST URL " + url); var response = await client.GetAsync(url);这里有两条传递路径,值得重点区分:
- 自动路径(示例实际采用):
AddHttpClientInstrumentation()启用后,OpenTelemetry .NET SDK 会使用默认的传播器组合CompositeTextMapPropagator(内含TraceContextPropagator与BaggagePropagator),在出站请求上自动注入baggage头。因此上面被注释掉的手动加头代码不需要启用,下游端点也能收到 Baggage。 - 手动路径(被注释的备选方案):
client.DefaultRequestHeaders.Add("baggage", baggage);直接复用上一节拼接好的 Baggage 字符串,把baggage头手动写到HttpClient的默认请求头上。这一行注释在源码中(Program.cs),说明在开启 HTTP 客户端探针后自动注入已足够;当你不想依赖探针、或需要精细控制头值时,可取消注释并移除探针作为降级方案。
3.4 接收端读取:/baggage 端点
app.MapGet("/baggage", (HttpRequest request) => { var baggage = request.Headers["baggage"]; app.Logger.LogInformation($"BAGGAGE VALUES {baggage}"); return Results.Ok(); });下游端点直接从请求头request.Headers["baggage"]取出 Baggage 值并写日志。整个示例形成一条完整回路:GET /写入 Baggage → 借助 HttpClient 以baggage头发给GET /baggage→ 接收端读取头并输出BAGGAGE VALUES ...。
四、运行与验证
在仓库中进入示例目录并启动(仓库根 global.json 指定 SDK 10.0.0):
dotnet run --project projects/open-telemetry/open-telemetry-4应用默认监听本地端口(参考同仓库 open-telemetry-2 的运行输出,地址为http://localhost:5000)。随后访问入口端点:
curl http://localhost:5000/浏览器/curl 会得到Check the log文本,真正的结果要看应用控制台日志。预期会依次出现两类日志(推断自 Program.cs 的逻辑):
info: ... REQUEST URL http://localhost:5000/baggage info: ... BAGGAGE VALUES project=practical-aspnetcore,location=CairoBAGGAGE VALUES一行即为验证结果:GET /中通过AddBaggage写入的project与location两个键值,已经以 W3Cbaggage头的形式传递到了/baggage端点并被成功解析。
五、Baggage 与 Span Attribute 的取舍
仓库中的 OTel 系列示例正好给出了同一组业务数据的两种记录方式:
- open-telemetry-3:用
Activity.Current?.SetTag("project", "practical-aspnetcore")写入Span Attribute,控制台导出的 Span 中出现project、location标签,但属性只留在本服务的当前 Span 内。 - open-telemetry-4(本文):用
AddBaggage写入Baggage,属性跟随baggage头跨服务传递。
实际选型建议:
- 需要在本服务 trace 中检索/过滤、且不希望泄漏给下游 → 用 Span Attribute。
- 需要随请求传给下游服务做决策或观测(如租户隔离、灰度标记)→ 用 Baggage。
- Baggage 会随每个出站请求传播,应避免放入敏感信息(如令牌、个人数据),并注意控制键值数量与大小,防止头膨胀影响链路性能。
- 若想在 Baggage 之外同时让下游能按业务维度聚合 trace,可结合 Attribute 一起记录,二者并不互斥。
六、小结
通过本示例可以确认一条完整链路:Activity.Current.AddBaggage写入业务上下文 →AddHttpClientInstrumentation按 W3C Baggage 规范自动注入baggage头 → 下游服务从请求头读取。这为 ASP.NET Core 分布式应用中"用户属性 / 租户信息等轻量上下文跨服务透传"提供了一个开箱即用的标准化方案,也为后续把 Baggage 与 OTel 的 trace、metrics 打通留出了空间。
- 示例工程
【免费下载链接】practical-aspnetcore
Practical samples of ASP.NET Core 11, 10, 9, 8.0, 7.0, 6.0, 5.0, 3.1, 2.2, and 2.1,projects you can use. Readme contains explanations on all projects.
相关推荐
whichllm跨平台支持:Windows、Linux与macOS硬件检测差异解析
whichllm跨平台支持:Windows、Linux与macOS硬件检测差异解析 whichllm是一款能够帮助用户找到在本地硬件上实际运行并表现最佳的LLM
人工智能大模型本地部署CLImirrord 会话上下文如何穿越对象存储:S3/GCS 签名上传链路的 baggage 传递实战
mirrord 会话上下文如何穿越对象存储:S3/GCS 签名上传链路的 baggage 传递实战 本文基于 mirrord Header Propagatio
开发工具云原生CLIOpenCOOD中的压缩技术:如何在有限带宽下实现高效协同感知
OpenCOOD中的压缩技术:如何在有限带宽下实现高效协同感知 OpenCOOD作为开源协同感知框架,通过先进的压缩技术解决了车辆间通信的带宽瓶颈问题,实现了在
自动驾驶人工智能计算机视觉
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考