🔭 Observability in ASP.NET: Seeing Inside Your App Like a Doctor with X-Ray Vision
The Hospital Analogy
Imagine your ASP.NET application is a patient in a hospital. When something goes wrong, you need to know:
- What’s happening right now? (Metrics - like heart rate, temperature)
- What happened step by step? (Tracing - like following the patient’s journey through the hospital)
OpenTelemetry is your magical medical toolkit that gives you X-ray vision into your application!
What is Observability?
Think of observability like being a detective with super powers:
Without Observability:
“My app is slow… somewhere… I think?” 😰
With Observability:
“Request #12345 took 3.2 seconds. It spent 2.8 seconds waiting for the database at line 47.” 🎯
The Three Pillars
graph TD A["🔭 Observability"] --> B["📊 Metrics"] A --> C["🔗 Traces"] A --> D["📝 Logs"] B --> E["Numbers over time"] C --> F["Request journey"] D --> G["Event messages"]
OpenTelemetry: Your Universal Translator
What is OpenTelemetry?
OpenTelemetry is like a universal language that all monitoring tools understand.
Simple Example:
- You speak English
- Your friend speaks Spanish
- Another friend speaks French
- OpenTelemetry is like having ONE translator who can talk to everyone!
Why Use OpenTelemetry?
| Old Way | OpenTelemetry Way |
|---|---|
| Different code for each tool | One code, any tool |
| Locked to one vendor | Switch anytime |
| Learn many libraries | Learn once |
Getting Started: Adding OpenTelemetry to ASP.NET
Step 1: Install the Packages
// In your terminal or package manager:
// dotnet add package
// OpenTelemetry.Extensions.Hosting
// dotnet add package
// OpenTelemetry.Instrumentation.AspNetCore
Step 2: Configure in Program.cs
builder.Services
.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation();
})
.WithMetrics(metrics =>
{
metrics
.AddAspNetCoreInstrumentation()
.AddRuntimeInstrumentation();
});
What this does:
- 🔗 Tracks every HTTP request (tracing)
- 📊 Counts requests and measures times (metrics)
- 🤖 Works automatically!
Metrics: Counting What Matters
What Are Metrics?
Metrics are numbers that describe your app over time.
Real-Life Examples:
| Metric | What It Measures |
|---|---|
http_requests_total |
How many requests came in |
http_request_duration |
How long requests take |
active_connections |
People connected right now |
Types of Metrics
graph TD A["📊 Metric Types"] --> B["Counter"] A --> C["Gauge"] A --> D["Histogram"] B --> E["Goes up only<br>Like total visitors"] C --> F["Goes up & down<br>Like current users"] D --> G["Distribution<br>Like response times"]
Creating Custom Metrics
// Create a meter (like a measuring tool)
var meter = new Meter("MyApp.Orders");
// Create a counter
var orderCounter = meter
.CreateCounter<int>("orders_placed");
// Use it!
orderCounter.Add(1); // +1 order placed!
Built-in ASP.NET Core Metrics
When you add AddAspNetCoreInstrumentation(), you get FREE metrics:
| Metric Name | What It Tells You |
|---|---|
http.server.duration |
Request response time |
http.server.active_requests |
Concurrent requests |
http.server.request.size |
Request body size |
Tracing: Following the Breadcrumbs
What is Tracing?
Tracing is like leaving breadcrumbs through your code so you can follow what happened.
Simple Example:
“Hansel and Gretel left breadcrumbs to find their way home. Tracing leaves digital breadcrumbs to find where your request went!”
The Journey of a Request
graph TD A["🌐 User Request"] --> B["🚪 API Gateway"] B --> C["🎯 Your Controller"] C --> D["💾 Database Call"] D --> E["📤 Response"] style A fill:#e1f5fe style E fill:#c8e6c9
Each arrow is a Span - one step in the journey!
Key Tracing Concepts
1. Trace = The entire journey (all breadcrumbs) 2. Span = One single step (one breadcrumb) 3. Context = The invisible thread connecting spans
Creating Custom Spans
// Create an ActivitySource (tracer)
var tracer = new ActivitySource("MyApp");
// Create a span for your operation
using var span = tracer.StartActivity(
"ProcessOrder");
// Add useful information
span?.SetTag("orderId", "12345");
span?.SetTag("customerName", "Alice");
// Do your work here...
// Span automatically ends when block exits
Adding Events to Spans
using var span = tracer.StartActivity(
"SendEmail");
span?.AddEvent(new ActivityEvent(
"Email validated"));
// Send the email...
span?.AddEvent(new ActivityEvent(
"Email sent successfully"));
Events are like sticky notes on your breadcrumbs!
Connecting Traces Across Services
Distributed Tracing
When your request travels across multiple services:
graph LR A["🌐 Web App"] --> B["🛒 Order Service"] B --> C["💳 Payment Service"] C --> D["📧 Email Service"]
OpenTelemetry automatically passes the Trace ID so everything connects!
How It Works
// Service A calls Service B
// OpenTelemetry adds special headers:
// traceparent: 00-abc123-def456-01
//
// Service B reads this header
// and continues the SAME trace!
Result: One request = One connected story across all services!
Exporting Your Data
Where Does the Data Go?
OpenTelemetry collects data, but you need to send it somewhere:
builder.Services
.AddOpenTelemetry()
.WithTracing(t => t
.AddConsoleExporter() // Print to console
.AddOtlpExporter()) // Send to collector
.WithMetrics(m => m
.AddPrometheusExporter() // For Prometheus
.AddOtlpExporter()); // Send to collector
Common Exporters
| Exporter | Best For |
|---|---|
| Console | Debugging locally |
| OTLP | Production (Jaeger, Zipkin) |
| Prometheus | Metrics dashboards |
Real-World Example: E-Commerce Order
Let’s trace an order from start to finish:
public async Task<Order> PlaceOrder(
OrderRequest request)
{
using var span = _tracer.StartActivity(
"PlaceOrder");
span?.SetTag("customerId",
request.CustomerId);
// Step 1: Validate
using (_tracer.StartActivity("ValidateOrder"))
{
ValidateOrder(request);
span?.AddEvent(new("Order validated"));
}
// Step 2: Process payment
using (_tracer.StartActivity("ProcessPayment"))
{
await _paymentService.Charge(request);
span?.AddEvent(new("Payment processed"));
}
// Step 3: Update inventory
using (_tracer.StartActivity("UpdateInventory"))
{
await _inventoryService.Reserve(request);
span?.AddEvent(new("Inventory updated"));
}
return order;
}
What you’ll see in your monitoring tool:
PlaceOrder (total: 450ms)
├── ValidateOrder (12ms)
├── ProcessPayment (320ms) ← The slow part!
└── UpdateInventory (118ms)
Now you know exactly where time is spent!
Quick Setup Checklist
graph TD A["1️⃣ Install Packages"] --> B["2️⃣ Configure Services"] B --> C["3️⃣ Add Instrumentation"] C --> D["4️⃣ Choose Exporter"] D --> E["5️⃣ View in Dashboard"]
Minimal Complete Setup
// Program.cs - Complete example
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenTelemetry()
.ConfigureResource(r => r
.AddService("MyWebApp"))
.WithTracing(t => t
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter())
.WithMetrics(m => m
.AddAspNetCoreInstrumentation()
.AddRuntimeInstrumentation()
.AddOtlpExporter());
var app = builder.Build();
app.Run();
Key Takeaways
| Concept | Remember This |
|---|---|
| Observability | Seeing inside your running app |
| OpenTelemetry | Universal language for monitoring |
| Metrics | Numbers over time (counting things) |
| Tracing | Following a request’s journey |
| Spans | Individual steps in a trace |
| Exporters | Where to send your data |
You Did It! 🎉
You now understand how to:
- ✅ Add OpenTelemetry to ASP.NET
- ✅ Collect metrics automatically
- ✅ Trace requests across services
- ✅ Create custom spans and metrics
- ✅ Export data to monitoring tools
Remember: Observability is like having night-vision goggles for your code. You can finally see what’s happening instead of guessing!
“The best time to add observability was yesterday. The second best time is now.” 🔭
