如何通过外部包为.NET WebAPI注入OpenTelemetry Jaeger链路追踪
为.NET 7 WebAPI通过外部包注入OpenTelemetry Jaeger追踪的解决方案
问题1:基于ActivitySource监听请求的方式是否正确?
这个方向是正确的。ActivitySource是OpenTelemetry .NET实现中生成、管理追踪跨度(Activity)的核心组件,完全符合OpenTelemetry的规范。你之前手动注入ActivitySource加using块的方式能生成有效追踪,但重复编写代码的问题可以通过自动化手段解决,本身思路没问题。
问题2:若正确,如何动态为每个请求方法启动追踪,避免重复编写using块?
可以通过全局Action过滤器+ActivitySource的组合实现自动化追踪,不需要在每个控制器方法里写重复代码,具体步骤如下:
- 在类库中实现自定义Action过滤器,自动处理Activity的创建和结束:
public class JaegerTracingFilter : ActionFilterAttribute { private readonly ActivitySource _activitySource; public JaegerTracingFilter(ActivitySource activitySource) { _activitySource = activitySource; } public override void OnActionExecuting(ActionExecutingContext context) { var request = context.HttpContext.Request; // 创建Activity,设置Kind为Server(表示服务端接收请求的追踪跨度) using var activity = _activitySource.StartActivity( $"{request.Method} {request.Path}", ActivityKind.Server); // 将Activity存入HttpContext,方便后续扩展(比如添加自定义标签) context.HttpContext.Items["CurrentTraceActivity"] = activity; // 添加自定义标签,比如请求路径、方法等 activity?.AddTag("http.method", request.Method); activity?.AddTag("http.path", request.Path); } public override void OnActionExecuted(ActionExecutedContext context) { if (context.HttpContext.Items.TryGetValue("CurrentTraceActivity", out var obj) && obj is Activity activity) { // 添加响应状态码标签 activity.AddTag("http.status_code", context.HttpContext.Response.StatusCode); // 如果有异常,标记为错误 if (context.Exception != null) { activity.SetStatus(ActivityStatusCode.Error); activity.RecordException(context.Exception); } } } }
- 在类库中提供扩展方法,让WebAPI只需调用一次即可完成追踪配置(包括注册过滤器、ActivitySource、OpenTelemetry和Jaeger导出):
public static class JaegerTracingExtensions { private const string ActivitySourceName = "YourCompany.WebApi.Tracing"; public static IServiceCollection AddJaegerTracing(this IServiceCollection services, params string[] targetAssemblies) { // 注册ActivitySource为单例 services.AddSingleton(new ActivitySource(ActivitySourceName)); // 注册全局Action过滤器,仅对指定程序集的控制器生效 services.AddMvc(options => { options.Filters.AddService<JaegerTracingFilter>(int.MinValue); }) .AddApplicationPart(Assembly.Load(targetAssemblies.First())); // 配置OpenTelemetry和Jaeger导出(复用你现有的控制台配置) services.AddOpenTelemetry() .WithTracing(tracerProviderBuilder => { tracerProviderBuilder .AddSource(ActivitySourceName) .AddJaegerExporter(options => { // 填入你的Jaeger配置 options.AgentHost = "localhost"; options.AgentPort = 6831; }); }); // 注册过滤器服务 services.AddScoped<JaegerTracingFilter>(); return services; } }
- WebAPI端只需在Program.cs中调用这个扩展方法即可,无需编写任何追踪相关的中间件或控制器代码:
var builder = WebApplication.CreateBuilder(args); // 调用外部类库的扩展方法,传入要追踪的程序集名称 builder.Services.AddJaegerTracing("YourWebApi.AssemblyName"); // 其他原有配置... var app = builder.Build(); // 原有中间件配置... app.Run();
这种方式完全满足需求:所有追踪逻辑都在外部类库中,WebAPI只需要一行代码调用,自动为所有请求方法生成包含Activity.Kind的完整追踪。
问题3:若该方式错误,可使用OpenTelemetry的哪些方法实现需求?
如果不想用Action过滤器,还可以通过DiagnosticListener监听ASP.NET Core原生诊断事件的方式实现,ASP.NET Core会自动发出请求相关的诊断事件,我们可以在类库中订阅这些事件来生成追踪:
- 在类库中实现DiagnosticListener订阅者:
public class AspNetCoreTraceSubscriber : IObserver<DiagnosticListener> { private readonly ActivitySource _activitySource; public AspNetCoreTraceSubscriber(ActivitySource activitySource) { _activitySource = activitySource; } public void OnNext(DiagnosticListener listener) { if (listener.Name == "Microsoft.AspNetCore") { listener.SubscribeWithAdapter(new HttpRequestObserver(_activitySource)); } } public void OnCompleted() { } public void OnError(Exception error) { } } public class HttpRequestObserver { private readonly ActivitySource _activitySource; public HttpRequestObserver(ActivitySource activitySource) { _activitySource = activitySource; } // 订阅请求开始事件 [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn.Start")] public void OnHttpRequestInStart(HttpContext context) { using var activity = _activitySource.StartActivity( $"{context.Request.Method} {context.Request.Path}", ActivityKind.Server); activity?.AddTag("http.method", context.Request.Method); activity?.AddTag("http.path", context.Request.Path); context.Items["CurrentTraceActivity"] = activity; } // 订阅请求结束事件 [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop")] public void OnHttpRequestInStop(HttpContext context) { if (context.Items.TryGetValue("CurrentTraceActivity", out var obj) && obj is Activity activity) { activity.AddTag("http.status_code", context.Response.StatusCode); } } }
- 同样在扩展方法中注册订阅者和OpenTelemetry配置:
public static IServiceCollection AddJaegerTracing(this IServiceCollection services) { var activitySource = new ActivitySource("YourCompany.WebApi.Tracing"); services.AddSingleton(activitySource); services.AddSingleton<IObserver<DiagnosticListener>, AspNetCoreTraceSubscriber>(); // 配置OpenTelemetry和Jaeger导出(同之前的代码) services.AddOpenTelemetry() .WithTracing(tracerProviderBuilder => { tracerProviderBuilder .AddSource(activitySource.Name) .AddJaegerExporter(options => { options.AgentHost = "localhost"; options.AgentPort = 6831; }); }); return services; }
这种方式不需要依赖Mvc过滤器,直接监听ASP.NET Core的原生事件,同样可以实现无侵入的追踪注入,适合不想依赖Mvc组件的场景。
内容的提问来源于stack exchange,提问作者Golide
相关产品推荐
相关产品推荐

