.NET Core  

OpenTelemetry in .NET 9: Trace Requests End-to-End with Jaeger

Introduction

In today’s world of modern distributed systems, it’s very important to understand how a request moves across different services, especially when you are fixing bugs or trying to make things faster.

With microservices, single-page apps (SPAs), asynchronous message queues, and multiple databases involved, tracking a single user’s request becomes quite challenging.

Fortunately, OpenTelemetry (OTel) has emerged as the open standard for collecting telemetry data, including traces, metrics, and logs. With .NET 9, developers can now easily add full tracing to their systems with even better support for OpenTelemetry, requiring minimal effort.

In this blog, we’ll see how you can trace a request starting from the browser, going through a .NET 9 backend, and all the way to the database using OpenTelemetry and Jaeger to view the trace data. We’ll also explain where the telemetry data is stored and how to maintain secure access to Jaeger in a production setup.

Why OpenTelemetry?

OpenTelemetry is an open-source toolset for observability, backed by the Cloud Native Computing Foundation (CNCF). It helps standardize how data, such as traces, logs, and metrics, is collected and shared across different systems.

Here are some key benefits.

  • Vendor-neutral: You can change your backend tools without affecting how you’ve written the monitoring code.
  • Works with many languages: Supports .NET, Java, JavaScript, Python, Go, and more.
  • Gives full context: You can capture traces, metrics, logs, baggage, and system details all together.

Whether you're building microservices, backend APIs, or frontend applications, OpenTelemetry provides a unified approach to understanding how your system operates in real-world scenarios.

Setting up Telemetry in .NET 9 Web API Project

Create a .NET 9 project and add the following NuGet packages to start capturing telemetry.

Install the necessary NuGet packages.

# In Visual Studio Code
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
dotnet add package OpenTelemetry.Instrumentation.EntityFrameworkCore
dotnet add package OpenTelemetry.Exporter.Jaeger

# In Visual Studio Package Manager Console
Install-Package OpenTelemetry.Extensions.Hosting
Install-Package OpenTelemetry.Instrumentation.AspNetCore
Install-Package OpenTelemetry.Instrumentation.Http
Install-Package OpenTelemetry.Instrumentation.EntityFrameworkCore
Install-Package OpenTelemetry.Exporter.Jaeger

Then in your Program.cs.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracerProviderBuilder =>
    {
        tracerProviderBuilder
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddEntityFrameworkCoreInstrumentation()
            .AddOtlpExporter(options =>
            {
                options.Endpoint = new Uri("http://localhost:4317");
            });
    });

This automatically captures.

  • Incoming HTTP requests to your APIs
  • Outgoing HTTP calls (to other APIs)
  • SQL queries via EF Core
  • Propagation of trace context (traceId, spanId)

Frontend Project Setup: Tracing from the Browser

OpenTelemetry also works well with JavaScript-based single-page applications (SPAs), such as React or Angular. It helps you track important user activities, such as.

  • User clicks: what users are clicking on.
  • Page loads: how fast your pages are loading.
  • API calls: how your frontend is talking to the backend.

With this, you get a clear picture of how users are interacting with your app and how your frontend is performing. This is particularly useful for resolving issues and enhancing the user experience.

Need to install the below NPM dependencies.

npm install \
  @opentelemetry/api \
  @opentelemetry/sdk-trace-web \
  @opentelemetry/exporter-otlp-http

Now to set up the same need to add the below code.

import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { OTLPTraceExporter } from '@opentelemetry/exporter-otlp-http';
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';

const provider = new WebTracerProvider();

provider.addSpanProcessor(
  new SimpleSpanProcessor(
    new OTLPTraceExporter({
      url: 'http://localhost:4318/v1/traces',
    })
  )
);

provider.register();

What is Jaeger?

Jaeger is an open-source tool for distributed tracing, originally developed by Uber. It helps you visualize how requests flow through various parts of your system.

Some of its key features.

  • A powerful and easy-to-use UI that shows traces, spans, and their timelines.
  • Works with various storage options, including Elasticsearch and others.
  • Easily connects with the OpenTelemetry Collector.

With Jaeger, you can gain a clear understanding.

  • How a user request travels across various services.
  • Which part of the system is taking more time?
  • Where errors are happening.
  • And how can you reduce delays (latency) to make your system faster?

It’s a great tool for debugging and improving performance in complex systems.

What You’ll See in Jaeger UI

Once you connect Jaeger to your system, it shows you a full trace of how a request flows, step by step. You can see.

  • A timeline of the request starting from the browser, moving to your API, and then reaching the database.
  • How long does each part of the request take, and how are different steps connected (parent-child structure)?
  • Practical details, such as the HTTP method, URL, and response status code.
  • The actual SQL query is being run from Entity Framework Core (EF Core).
  • Exceptions and error messages are captured as part of the trace.
  • Extra information like tags, logs, and attributes (e.g., user.id, environment, region, etc.).

All this makes it very easy to identify slow parts, fix bugs, or understand failures in complex systems that involve multiple services.

Where is Telemetry Data Stored?

Many people think that OpenTelemetry stores traces in the same SQL Server or Oracle database used by your app, but that’s not true.

By default, Jaeger saves trace data either in memory (which is temporary) or on the local disk using Badger; both are suitable for testing, but not for production.

For production environments, you should set up one of the supported storage systems.

Supported storage backends.

  • Elasticsearch: Most popular, good for searching and scaling
  • Apache Cassandra: Good for handling large amounts of data with horizontal scaling
  • Google Cloud Trace / AWS X-Ray: Great if you're using cloud platforms
  • Kafka + Elasticsearch: Helps buffer and process large trace data smoothly
  • ClickHouse / PostgreSQL: Available via community plugins using OpenTelemetry Collector

You don’t configure this in your app’s database. Instead, you set it in the OpenTelemetry Collector or Jaeger settings. The right choice depends on your team’s infrastructure, data size, and how you plan to search or analyse the trace data.

Securing Jaeger in Production

By default, Jaeger’s web UI is open to everyone. There is no login, no protection, which is risky if you’re running it in a real environment (like on the cloud or an internal network). Therefore, it is essential to add security before going live.

Option 1. Use Basic Authentication with IIS

If you’re hosting Jaeger behind IIS, you can enable Basic Auth like this.

  • Host Jaeger on IIS using reverse proxy (e.g., Application Request Routing + URL Rewrite).
  • Enable Basic Authentication in IIS for the Jaeger app or site.
  • Create a local user or add .htpasswd-like authentication using Windows Users.
  • Add a rule to forward /jaeger to Jaeger’s backend (default port is 16686).
    <rule name="ReverseProxyToJaeger">
      <match url="^jaeger/(.*)" />
      <action type="Rewrite" url="http://localhost:16686/{R:1}" />
    </rule>
    

Now, whenever someone accesses http://localhost/jaeger, IIS will ask for a username/password. You can easily manage users via Windows Authentication or use the Basic Auth module with custom users.

Option 2. Use OAuth2 Proxy with SSO (Google, GitHub, Azure AD)

For more advanced setups, you can use OAuth2 Proxy.

Here’s what to do?

  1. Deploy OAuth2 Proxy as a sidecar or service.
  2. Connect it to your company’s SSO provider (like Google, Azure Active Directory, GitHub, Octa, or any other).
  3. Configure Jaeger behind this proxy, so only logged-in users from your org can access it.

This setup provides enterprise-level access control, allowing only authorized employees to access the Jaeger UI.

Conclusion

With .NET and OpenTelemetry, it has become significantly easier to perform full-stack tracing. You can track everything, from what the user is doing in the browser to how your backend API is working and even how your SQL queries are performing.

All this information is connected into one smooth, complete trace, so you can see the full journey of a request.

Jaeger works like your control panel or dashboard. It shows you everything in one place.

  • Live view of how your system is behaving
  • Where exactly are things going wrong
  • Helps you confidently handle issues in your production environment

This makes it very simple to monitor, debug, and optimize your applications from the frontend to the backend to the database, all in one view.