Modern distributed applications are composed of multiple services, APIs, databases, and external dependencies. When a request fails or performance degrades, identifying the root cause becomes challenging without proper observability.

Traditional logging alone is no longer sufficient. Developers need visibility into how requests flow across services, where latency occurs, and which components contribute to failures. This is where OpenTelemetry comes in.

OpenTelemetry is an open-source observability framework that standardizes the collection of logs, metrics, and distributed traces. It enables developers to monitor applications consistently while remaining vendor-neutral.

In this guide, you'll learn how to integrate OpenTelemetry into an ASP.NET Core application and adopt best practices for production environments.

What Is OpenTelemetry?

OpenTelemetry (OTel) provides a unified way to collect telemetry data from applications.

It focuses on three pillars of observability:

Together, these signals help answer critical questions such as:

Unlike vendor-specific SDKs, OpenTelemetry allows you to send telemetry to multiple monitoring platforms without changing application code.

Understanding the OpenTelemetry Architecture

A typical OpenTelemetry workflow looks like this:

ASP.NET Core Application
        │
OpenTelemetry SDK
        │
Telemetry Data
        │
OpenTelemetry Collector (Optional)
        │
Monitoring Platform

The SDK collects telemetry, while the optional OpenTelemetry Collector processes and exports data to systems like Azure Monitor, Jaeger, Prometheus, Grafana, or Elastic.

Installing OpenTelemetry

Install the required NuGet packages:

dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
dotnet add package OpenTelemetry.Instrumentation.SqlClient
dotnet add package OpenTelemetry.Exporter.Console

These packages enable automatic instrumentation for common ASP.NET Core components.

Configuring OpenTelemetry

Register OpenTelemetry during application startup.

using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .ConfigureResource(resource =>
        resource.AddService("ProductApi"))
    .WithTracing(tracing =>
    {
        tracing
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddSqlClientInstrumentation()
            .AddConsoleExporter();
    });

var app = builder.Build();

app.MapControllers();

app.Run();

This configuration automatically captures incoming HTTP requests, outgoing HTTP calls, SQL queries, and exports trace information to the console.

Collecting Metrics

Metrics help monitor application performance over time.

Configure metrics collection:

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics
            .AddAspNetCoreInstrumentation()
            .AddRuntimeInstrumentation()
            .AddConsoleExporter();
    });

Common metrics include:

These measurements help identify trends before they become production issues.

Understanding Distributed Tracing

Distributed tracing follows a request as it moves through different services.

Example flow:

Client
   │
Product API
   │
Order API
   │
SQL Server

Each operation becomes a span, while the complete request forms a trace.

If a request takes five seconds, tracing reveals how much time was spent in:

This makes performance bottlenecks much easier to identify.

Adding Custom Traces

Automatic instrumentation covers common scenarios, but custom business operations can also be traced.

using System.Diagnostics;

private static readonly ActivitySource ActivitySource =
    new("ProductService");

public async Task<Product> GetProductAsync(int id)
{
    using var activity = ActivitySource.StartActivity("Get Product");

    return await repository.GetByIdAsync(id);
}

Custom spans provide visibility into business workflows that automatic instrumentation cannot detect.

Choosing an Exporter

OpenTelemetry supports multiple exporters depending on your monitoring platform.

ExporterBest Use Case
ConsoleLocal development
JaegerDistributed tracing
PrometheusMetrics collection
Azure MonitorAzure-hosted applications
OTLPVendor-neutral export

Using the OTLP exporter provides flexibility to switch monitoring platforms without modifying application instrumentation.

Best Practices

Common Mistakes

Relying Only on Logs

Logs explain what happened, but traces reveal where it happened and metrics show how often it occurs. Combining all three signals provides a complete picture.

Instrumenting Everything

Capturing every operation generates excessive telemetry, increasing storage costs and making analysis more difficult. Focus on critical application paths.

Ignoring Sampling

High-volume production systems can generate millions of traces. Configure sampling strategies to balance observability with operational cost.

Exposing Sensitive Data

Avoid recording passwords, authentication tokens, personal information, or confidential business data in logs or traces.

Conclusion

OpenTelemetry has become the standard for observability in modern ASP.NET Core applications. By combining logs, metrics, and distributed traces, it enables developers to understand application behavior, diagnose failures, and optimize performance across distributed systems.

Automatic instrumentation provides immediate visibility into HTTP requests, database operations, and external service calls, while custom spans allow teams to monitor business-specific workflows. Combined with appropriate exporters and thoughtful sampling strategies, OpenTelemetry offers a scalable, vendor-neutral approach to monitoring applications in development and production.

Implementing observability early in the development lifecycle not only simplifies troubleshooting but also helps build resilient, high-performing .NET applications that are easier to maintain as they grow.