.NET Core  

OpenTelemetry in .NET: End-to-End Distributed Tracing Explained

Introduction

Modern applications rarely exist as a single monolithic system. Most organizations now build applications using microservices, cloud-native architectures, APIs, message queues, databases, and third-party services. While these architectures improve scalability and flexibility, they also introduce new challenges when diagnosing performance issues and troubleshooting failures.

Imagine a user request that travels through an API Gateway, multiple microservices, a database, a cache, and several external services. When something goes wrong, identifying the root cause can be difficult without proper observability.

This is where OpenTelemetry becomes essential. OpenTelemetry provides a standardized way to collect telemetry data, including traces, metrics, and logs, helping developers understand how requests flow through distributed systems.

In this article, you'll learn how distributed tracing works, how OpenTelemetry integrates with .NET applications, and how to implement end-to-end tracing for better observability and monitoring.

What Is OpenTelemetry?

OpenTelemetry is an open-source observability framework designed to collect, process, and export telemetry data from applications and infrastructure.

It provides support for:

  • Distributed tracing

  • Metrics collection

  • Log correlation

  • Application monitoring

  • Performance analysis

OpenTelemetry is vendor-neutral, meaning telemetry data can be exported to various monitoring platforms without changing application code.

Popular integrations include:

  • Azure Monitor

  • Application Insights

  • Jaeger

  • Zipkin

  • Prometheus

  • Grafana

  • Datadog

  • New Relic

This flexibility has made OpenTelemetry the industry standard for observability.

Understanding Distributed Tracing

Distributed tracing tracks a request as it moves through different components of a system.

Consider the following architecture:

Client
  ↓
API Gateway
  ↓
Order Service
  ↓
Payment Service
  ↓
Database

Without tracing, determining where latency occurs is difficult.

Distributed tracing allows developers to see:

  • Request flow

  • Execution time

  • Dependencies

  • Failures

  • Bottlenecks

This visibility is critical in modern distributed applications.

Core OpenTelemetry Concepts

Before implementing OpenTelemetry, it's important to understand its primary components.

Trace

A trace represents the complete journey of a request.

Example:

Create Order Request

A single trace may span multiple services and systems.

Span

A span represents a single operation within a trace.

Example:

Create Order
    ↓
Validate Payment
    ↓
Save Database Record

Each operation becomes a separate span.

Context Propagation

Context propagation allows trace information to move across services.

This enables OpenTelemetry to reconstruct the complete request path.

Exporter

An exporter sends telemetry data to monitoring systems.

Examples include:

  • Jaeger Exporter

  • Zipkin Exporter

  • Azure Monitor Exporter

  • OTLP Exporter

Why OpenTelemetry Matters in .NET Applications

Traditional logging provides useful information, but it often lacks context across distributed systems.

OpenTelemetry provides:

  • End-to-end visibility

  • Faster troubleshooting

  • Improved performance analysis

  • Better dependency tracking

  • Unified observability

For enterprise .NET applications, observability is no longer optional.

Setting Up OpenTelemetry in .NET

Create a new ASP.NET Core application:

dotnet new webapi -n OpenTelemetryDemo

Install the required packages:

dotnet add package OpenTelemetry.Extensions.Hosting

dotnet add package OpenTelemetry.Exporter.Console

dotnet add package OpenTelemetry.Instrumentation.AspNetCore

dotnet add package OpenTelemetry.Instrumentation.Http

These packages provide tracing capabilities and telemetry export functionality.

Configure OpenTelemetry

Add OpenTelemetry configuration in Program.cs.

using OpenTelemetry.Trace;

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddConsoleExporter();
    });

This configuration automatically captures:

  • Incoming HTTP requests

  • Outgoing HTTP requests

  • Trace information

Telemetry is displayed in the console.

Automatic Instrumentation

One of OpenTelemetry's biggest advantages is automatic instrumentation.

When enabled, many operations are traced automatically.

Example:

app.MapGet("/products", () =>
{
    return Results.Ok("Products Retrieved");
});

When a request reaches this endpoint, OpenTelemetry automatically generates trace data.

No additional code is required.

Creating Custom Spans

Developers often need visibility into business operations.

Custom spans provide this capability.

Example:

using System.Diagnostics;

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

using var activity =
    ActivitySource.StartActivity(
        "ProcessOrder");

Additional metadata can be attached.

activity?.SetTag(
    "order.id",
    1001);

Custom spans improve trace detail and troubleshooting accuracy.

Tracing Database Operations

Database interactions are often performance bottlenecks.

Install SQL instrumentation:

dotnet add package OpenTelemetry.Instrumentation.SqlClient

Configure tracing:

tracing.AddSqlClientInstrumentation();

OpenTelemetry automatically captures:

  • SQL execution time

  • Database dependencies

  • Query latency

This helps identify slow database operations.

Tracing Outgoing HTTP Requests

Many applications call external APIs.

Example:

var client = new HttpClient();

var response =
    await client.GetAsync(
        "https://api.example.com");

With HttpClient instrumentation enabled:

tracing.AddHttpClientInstrumentation();

OpenTelemetry records:

  • Request duration

  • Response status

  • External dependencies

This provides visibility into third-party service performance.

Exporting Traces to Jaeger

Jaeger is a popular distributed tracing platform.

Install exporter:

dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol

Configure exporter:

tracing.AddOtlpExporter(options =>
{
    options.Endpoint =
        new Uri(
            "http://localhost:4317");
});

Jaeger provides a visual representation of request traces.

Developers can quickly identify bottlenecks and failures.

Correlating Logs and Traces

One challenge in distributed systems is connecting logs with traces.

OpenTelemetry provides trace identifiers that can be included in logs.

Example:

_logger.LogInformation(
    "Processing order {OrderId}",
    orderId);

When trace IDs are attached, developers can easily correlate:

  • Logs

  • Metrics

  • Traces

This dramatically improves troubleshooting efficiency.

Monitoring Microservices

Consider a microservices architecture:

API Gateway
    ↓
Order Service
    ↓
Inventory Service
    ↓
Payment Service
    ↓
Database

Without OpenTelemetry:

  • Difficult troubleshooting

  • Limited visibility

  • Slow root-cause analysis

With OpenTelemetry:

  • Complete request flow visibility

  • Latency breakdowns

  • Dependency mapping

  • Faster incident resolution

This is one of the primary reasons organizations adopt distributed tracing.

Practical Example

A customer submits an order.

Trace output:

Trace ID:
123456

Create Order
  120ms

Validate Payment
  80ms

Save Database Record
  45ms

Send Confirmation Email
  350ms

The trace reveals that email processing introduces the most latency.

Developers can immediately focus optimization efforts on the correct component.

Without tracing, identifying this bottleneck could take significantly longer.

Best Practices

When implementing OpenTelemetry in .NET applications:

  • Define meaningful span names.

  • Add useful metadata through tags.

  • Instrument critical business operations.

  • Avoid collecting sensitive information.

  • Use sampling for high-volume workloads.

  • Standardize telemetry across services.

  • Monitor trace storage costs.

  • Correlate logs, metrics, and traces.

These practices improve observability while maintaining performance.

Common Mistakes to Avoid

Avoid these common pitfalls:

  • Tracing every operation unnecessarily.

  • Storing sensitive customer data in traces.

  • Ignoring trace sampling strategies.

  • Creating inconsistent span names.

  • Failing to monitor telemetry overhead.

  • Not correlating traces with logs.

A well-designed observability strategy balances visibility and performance.

Real-World Use Cases

OpenTelemetry is widely used for:

  • Microservices monitoring

  • Cloud-native applications

  • Distributed APIs

  • Event-driven systems

  • Kubernetes workloads

  • SaaS platforms

  • E-commerce applications

  • Enterprise software

Organizations use OpenTelemetry to improve reliability, reduce downtime, and accelerate troubleshooting.

Conclusion

OpenTelemetry has become the standard solution for observability in modern distributed systems. By providing end-to-end distributed tracing, it enables developers to understand how requests move through applications, identify bottlenecks, and diagnose issues more effectively.

For .NET applications, OpenTelemetry offers seamless integration with ASP.NET Core, HttpClient, databases, and external services. Combined with monitoring platforms such as Jaeger, Grafana, Azure Monitor, and Application Insights, it provides the visibility needed to operate complex cloud-native applications confidently.

As software architectures continue to become more distributed, implementing OpenTelemetry is one of the most valuable investments organizations can make in application reliability, performance, and operational excellence.