mcpField
C
configuring-opentelemetry-dotnet

configuring-opentelemetry-dotnet

github.com/dotnet开发5.8k3.4k 次安装1 天前更新
代码仓库

关于 configuring-opentelemetry-dotnet

configuring-opentelemetry-dotnet 是配置 .NET OpenTelemetry 的技能,面向需要为 ASP.NET Core 应用添加可观测性的开发者。它涵盖追踪、指标、日志关联、自定义埋点和上下文传播。

主要功能

  1. 01配置追踪、指标与日志导出
  2. 02关联日志与追踪上下文
  3. 03创建自定义 Activity 与指标
  4. 04设置分布式上下文传播
  5. 05提供配置验证与常见问题排查

适用场景

  1. 01为 ASP.NET Core 添加分布式追踪
  2. 02通过 OTLP 导出可观测性数据
  3. 03为业务操作添加自定义跨度和指标
  4. 04排查服务间追踪上下文传播

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dotnet/skills --skill configuring-opentelemetry-dotnet

安装后在 Claude Code 中输入/configuring-opentelemetry-dotnet确认已加载。

详细介绍

在 .NET 中配置 OpenTelemetry

适用场景

  • 为 ASP.NET Core 应用添加分布式追踪
  • 设置 OpenTelemetry 导出器(OTLP 是主要协议;Jaeger 原生支持 OTLP;Prometheus 的 OTLP 数据接收需要显式启用)
  • 为业务操作创建自定义指标或追踪跨度
  • 排查跨服务的分布式追踪上下文传播问题

不适用场景

  • 用户只需要应用级日志记录(使用 ILogger、Serilog)
  • 用户直接使用 Application Insights SDK(API 不同)
  • 用户需要使用商业供应商专有 SDK 的 APM

输入

输入必需描述
ASP.NET Core 项目是要进行插桩的应用
可观测性后端否数据导出目标:OTLP 收集器、Aspire 仪表板、Jaeger(原生支持 OTLP)

工作流

步骤 1:安装正确的程序包

OpenTelemetry NuGet 程序包种类繁多。请仅安装以下程序包:

# Core SDK + ASP.NET Core instrumentation + logging integration
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http

# Exporter
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol  # OTLP exporter for traces, metrics, AND logs

# Optional — dev/local debugging only (do NOT include in production deployments)
# dotnet add package OpenTelemetry.Exporter.Console

不要只安装 OpenTelemetry——还需要 OpenTelemetry.Extensions.Hosting,才能正确集成 DI。

可选:其他自动插桩程序包

只安装与你的应用所用库相匹配的程序包:

dotnet add package OpenTelemetry.Instrumentation.SqlClient           # SQL Server queries
dotnet add package OpenTelemetry.Instrumentation.EntityFrameworkCore  # EF Core
dotnet add package OpenTelemetry.Instrumentation.GrpcNetClient       # gRPC calls
dotnet add package OpenTelemetry.Instrumentation.Runtime             # GC, thread pool metrics
步骤 2:在 Program.cs 中配置所有信号
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using OpenTelemetry.Metrics;
using OpenTelemetry.Logs;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .ConfigureResource(resource => resource
        .AddService(serviceName: builder.Environment.ApplicationName))
    .WithTracing(tracing => tracing
        .AddAspNetCoreInstrumentation(options =>
        {
            // Filter out health check endpoints from traces
            options.Filter = httpContext =>
                !httpContext.Request.Path.StartsWithSegments("/healthz");
        })
        .AddHttpClientInstrumentation(options =>
        {
            options.RecordException = true;
        })
        // Optional: add SQL instrumentation if using SqlClient directly
        // .AddSqlClientInstrumentation(options =>
        // {
        //     options.SetDbStatementForText = true;
        //     options.RecordException = true;
        // })
        // Custom activity sources (must match ActivitySource names in your code)
        .AddSource("MyApp.Orders")
        .AddSource("MyApp.Payments")
        .AddSource("MyApp.Messaging"))
    .WithMetrics(metrics => metrics
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        // Optional: .AddRuntimeInstrumentation() for GC and thread pool metrics
        //   (requires OpenTelemetry.Instrumentation.Runtime package)
        // Custom meters (must match Meter names in your code)
        .AddMeter("MyApp.Metrics"))
    .WithLogging(logging =>
    {
        logging.IncludeScopes = true;
        // logging.IncludeFormattedMessage = true;  // Enable if you need the formatted message string in log exports
    })
    // Single OTLP exporter for all signals — reads OTEL_EXPORTER_OTLP_ENDPOINT
    // env var (defaults to http://localhost:4317). Override via environment variable
    // or appsettings.json configuration.
    .UseOtlpExporter();
步骤 3:了解日志与追踪的关联

步骤 2 中的 .WithLogging() 调用会将 ILogger 与 OpenTelemetry 集成:

  • 每条日志记录都会自动包含 TraceId 和 SpanId,以便与追踪关联
  • .ConfigureResource() 中配置的服务资源会自动传播到日志
  • UseOtlpExporter() 同样适用于日志和指标,不仅限于追踪
  • 无需额外程序包或单独调用 SetResourceBuilder
步骤 4:为业务操作创建自定义跨度(Activities)
using System.Diagnostics;
using Microsoft.Extensions.Logging;

public class OrderService
{
    // Create an ActivitySource matching what you registered in Step 2
    private static readonly ActivitySource ActivitySource = new("MyApp.Orders");
    private readonly ILogger<OrderService> _logger;

    public OrderService(ILogger<OrderService> logger) => _logger = logger;

    public async Task<Order> ProcessOrderAsync(CreateOrderRequest request)
    {
        // Start a new span
        using var activity = ActivitySource.StartActivity("ProcessOrder");

        // Add attributes (tags) to the span
        activity?.SetTag("order.customer_id", request.CustomerId);
        activity?.SetTag("order.item_count", request.Items.Count);

        try
        {
            // Child span for validation
            using (var validationActivity = ActivitySource.StartActivity("ValidateOrder"))
            {
                await ValidateOrderAsync(request);
                validationActivity?.SetTag("validation.result", "passed");
            }

            // Child span for payment
            using (var paymentActivity = ActivitySource.StartActivity("ProcessPayment",
                ActivityKind.Client))  // Client = outgoing call
            {
                paymentActivity?.SetTag("payment.method", request.PaymentMethod);
                await ProcessPaymentAsync(request);
            }

            var order = new Order { Id = Guid.NewGuid(), CustomerId = request.CustomerId, Status = "Completed" };

            activity?.SetTag("order.status", "completed");
            activity?.SetStatus(ActivityStatusCode.Ok);

            return order;
        }
        catch (Exception ex)
        {
            activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
            // Log via ILogger — OpenTelemetry captures this with trace correlation.
            // Prefer logging over activity.RecordException() as OTel is deprecating
            // span events for exception recording in favor of log-based exceptions.
            _logger.LogError(ex, "Order processing failed for customer {CustomerId}", request.CustomerId);
            throw;
        }
    }
}

重要:ActivitySource 名称必须与配置中的 AddSource("...") 相匹配。 不匹配的来源会被静默忽略——这是最常见的调试问题。

步骤 5:创建自定义指标

使用 IMeterFactory(通过 DI 注入)创建计量器,以确保生命周期管理得当且便于测试。

using System.Diagnostics;
using System.Diagnostics.Metrics;

public class OrderMetrics
{
    private readonly Counter<long> _ordersProcessed;
    private readonly Histogram<double> _orderProcessingDuration;
    private readonly UpDownCounter<int> _activeOrders;

    public OrderMetrics(IMeterFactory meterFactory)
    {
        // Meter name must match AddMeter("...") in configuration
        var meter = meterFactory.Create("MyApp.Metrics");

        // Counter — use for things that only go up
        _ordersProcessed = meter.CreateCounter<long>(
            "orders.processed", "orders", "Total orders successfully processed");

        // Histogram — use for measuring distributions (latency, sizes)
        _orderProcessingDuration = meter.CreateHistogram<double>(
            "orders.processing_duration", "ms", "Time to process an order");

        // UpDownCounter — use for things that go up AND down
        _activeOrders = meter.CreateUpDownCounter<int>(
            "orders.active", "orders", "Currently processing orders");
    }

    public void RecordOrderProcessed(string region, double durationMs)
    {
        // Tags enable dimensional filtering (by region, status, etc.)
        var tags = new TagList
        {
            { "region", region },
            { "order.type", "standard" }
        };

        _ordersProcessed.Add(1, tags);
        _orderProcessingDuration.Record(durationMs, tags);
    }
}

在 DI 中注册 OrderMetrics:

builder.Services.AddSingleton<OrderMetrics>();
步骤 6:为分布式场景配置上下文传播

使用 AddHttpClientInstrumentation() 时,HTTP 调用会自动传播追踪上下文。对于非 HTTP 场景:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using OpenTelemetry.Context.Propagation;

// ActivitySource should be static — register via .AddSource("MyApp.Messaging") in Step 2
private static readonly ActivitySource MessageSource = new("MyApp.Messaging");

// Manual context propagation (e.g., across message queues)
// On the SENDING side:
var propagator = Propagators.DefaultTextMapPropagator;
var activityContext = Activity.Current?.Context ?? default;
var context = new PropagationContext(activityContext, Baggage.Current);
var carrier = new Dictionary<string, string>();

propagator.Inject(context, carrier, (dict, key, value) => dict[key] = value);
// Send carrier dictionary as message headers

// On the RECEIVING side:
var parentContext = propagator.Extract(default, carrier,
    (dict, key) => dict.TryGetValue(key, out var value) ? new[] { value } : Array.Empty<string>());

Baggage.Current = parentContext.Baggage;
using var activity = MessageSource.StartActivity("ProcessMessage",
    ActivityKind.Consumer,
    parentContext.ActivityContext);  // Links to parent trace!

验证

  • 追踪数据出现在可观测性后端(Jaeger、Aspire 仪表板等)
  • HTTP 请求会自动创建包含正确动词、URL 和状态码的跨度
  • 自定义 ActivitySource 名称与 AddSource() 注册项相匹配
  • 自定义 Meter 名称与 AddMeter() 注册项相匹配
  • 日志包含 TraceId 和 SpanId,以便进行关联
  • 健康检查端点已从追踪中筛除
  • 错误跨度中包含异常详情

常见问题

问题解决方案
ActivitySource.StartActivity 返回 null来源名称与任何 AddSource() 都不匹配——名称必须完全一致
导出器中没有追踪数据检查 OTLP 端点:gRPC 使用端口 4317,HTTP 使用端口 4318
缺少 HTTP 客户端跨度确保已注册 AddHttpClientInstrumentation();它适用于 IHttpClientFactory/DI 和 new HttpClient()(为管理生命周期,请使用 IHttpClientFactory)
标签基数过高不要将用户 ID、请求 ID 或 UUID 用作指标标签——这会导致存储量激增
OTLP 的 gRPC 与 HTTP 不匹配默认使用 gRPC(端口 4317);如果收集器只接受 HTTP,请设置 OtlpExportProtocol.HttpProtobuf
Meter / ActivitySource 生命周期ActivitySource 应为静态;通过 DI 中的 IMeterFactory 创建 Meter(不要使用 new Meter()),以确保生命周期管理得当且便于测试

评论

支持 Markdown