Modern distributed applications are significantly more complex than traditional monolithic systems. A single user request may pass through multiple microservices, databases, caches, message brokers, and external APIs before returning a response. When performance issues or failures occur, simply reviewing application logs is rarely enough to identify the root cause.
Observability provides a comprehensive view of application behavior by combining logs, metrics, and distributed traces. .NET Aspire simplifies implementing observability by integrating OpenTelemetry into the application lifecycle, enabling developers to monitor distributed systems without extensive custom configuration.
In this article, you'll learn how to implement observability in .NET Aspire using OpenTelemetry, understand the role of logs, metrics, and traces, and build a production-ready monitoring pipeline.
Understanding Observability
What Is Observability?
Observability is the ability to understand the internal state of an application by analyzing the telemetry it produces.
Modern observability consists of three primary pillars:
Logs – Detailed records of application events.
Metrics – Numeric measurements collected over time.
Distributed Traces – The complete journey of a request across multiple services.
Together, these signals provide enough context to detect, investigate, and resolve production issues quickly.
Why Observability Matters
Imagine an e-commerce application consisting of several services:
Client
│
▼
API Gateway
│
├────────► Product Service
│
├────────► Inventory Service
│
├────────► Payment Service
│
└────────► Notification Service
A customer reports that placing an order takes 12 seconds.
Without observability, developers may spend hours searching logs across multiple services.
With distributed tracing and metrics, they can immediately identify that the delay originates from the Payment Service calling an external payment gateway.
Observability significantly reduces troubleshooting time and improves system reliability.
Configuring .NET Aspire for OpenTelemetry
.NET Aspire includes built-in support for OpenTelemetry.
Configure services during application startup.
var builder = DistributedApplication.CreateBuilder(args);
builder.AddProject<Projects.OrderApi>("orders");
builder.Build().Run();
Why This Configuration?
The distributed application builder establishes communication between application projects and enables Aspire to automatically configure telemetry collection for participating services.
Instead of configuring each microservice independently, Aspire centralizes the setup, reducing duplication and configuration errors.
Adding OpenTelemetry
Register OpenTelemetry services in your application.
builder.Services
.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation();
tracing.AddHttpClientInstrumentation();
})
.WithMetrics(metrics =>
{
metrics.AddAspNetCoreInstrumentation();
metrics.AddRuntimeInstrumentation();
});
Why Use Automatic Instrumentation?
Automatic instrumentation captures telemetry from ASP.NET Core and HttpClient without requiring developers to manually instrument every request.
This provides immediate visibility into incoming requests, outgoing HTTP calls, and runtime behavior while keeping application code clean.
Collecting Structured Logs
Logging remains one of the most valuable diagnostic tools.
app.Logger.LogInformation(
"Order {OrderId} created for customer {CustomerId}",
order.Id,
order.CustomerId);
Why Use Structured Logging?
Structured logs separate message templates from data values.
Benefits include:
Easier searching
Better filtering
Improved dashboards
More effective alerting
Avoid string concatenation because structured logging platforms cannot efficiently index plain text.
Recording Custom Metrics
Application-specific metrics provide valuable operational insights.
private static readonly Meter Meter =
new("OrderService");
private static readonly Counter<int> OrdersCreated =
Meter.CreateCounter<int>("orders.created");
Increment the metric after creating an order.
OrdersCreated.Add(1);
Why Create Custom Metrics?
Runtime metrics provide CPU and memory usage, but business metrics answer operational questions such as:
How many orders were processed?
How many invoices failed?
How many users signed in?
Custom metrics allow engineering teams to monitor business activity alongside infrastructure health.
Implementing Distributed Tracing
Distributed tracing follows requests across service boundaries.
private static readonly ActivitySource ActivitySource =
new("OrderService");
Creating an activity:
using var activity =
ActivitySource.StartActivity("Process Order");
Why Use Activities?
Activities create spans within distributed traces.
When one service calls another, trace context is automatically propagated, allowing developers to visualize the complete request lifecycle across the distributed application.
This dramatically simplifies diagnosing latency issues.
End-to-End Implementation
Consider an online retail platform built with .NET Aspire.
Architecture:
Customer
│
▼
Frontend
│
▼
Order API
│
┌───┴───────────────┐
▼ ▼
Inventory Payment Service
│ │
└──────────┬────────┘
▼
Notification Service
Workflow:
Join the conversation! Your thoughts help the community grow.