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();

Tuhin PaulPosted Jul 14, 2025, 2:50 AM
Thanks for the article. This is really concise and useful resource for developers looking to improve observability in distributed systems.