How-to · OpenTelemetry

How to Instrument .NET Apps with OpenTelemetry

Instrumenting .NET with OpenTelemetry means using the OpenTelemetry .NET SDK and instrumentation libraries, or the automatic instrumentation agent, to emit traces, metrics and logs over OTLP..

Instrumenting .NET with OpenTelemetry means using the OpenTelemetry .NET SDK and instrumentation libraries, or the automatic instrumentation agent, to emit traces, metrics and logs over OTLP.

What to observe in a .NET service

For a .NET service you watch per-endpoint latency and error rate, database and downstream call latency, and the CLR runtime: garbage collection, the thread pool, and exceptions. The .NET-specific failure that trips teams up is thread-pool starvation, usually from blocking on async code (sync-over-async), which makes a service slow and unresponsive while CPU and memory look fine.

The correlation that pinpoints most .NET incidents is endpoint latency against thread-pool queue length and GC: latency that rises with a growing thread-pool queue is starvation, and latency that lines up with gen-2 collections is heap pressure.

How OpenTelemetry collects it

.NET has first-class OpenTelemetry support. The SDK integrates with the built-in Activity tracing and the .NET metrics APIs, and instrumentation packages cover ASP.NET Core, HttpClient and common data access, so you add them and configure an OTLP exporter with AddOpenTelemetry(). For zero-code, the OpenTelemetry .NET automatic instrumentation agent injects the same instrumentation without editing the app.

The runtime metrics, GC collections and heap size, thread-pool thread and queue counts, and exception counts, come from the runtime instrumentation and are the .NET-specific half of the picture. The detail to watch is not double-emitting metrics if you run both the agent and manual SDK registration; pick one source per signal.

Runtime metric names follow the OpenTelemetry .NET conventions and can vary by version; confirm against your build.

Key metrics to watch

The signals that explain most .NET service incidents:

SignalSourceWhat it tells you / watch for
http.server.request.durationASP.NET Core instrumentationPer-endpoint latency histogram; track p95/p99 by route.
process.runtime.dotnet.thread_pool.queue.lengthruntime instrumentationA growing queue is thread-pool starvation, the classic sync-over-async symptom.
process.runtime.dotnet.gc.heap.size (gen 2)runtime instrumentationGen-2 heap that keeps growing after collection points to a leak.
process.runtime.dotnet.gc.collections.countruntime instrumentationFrequent gen-2 collections indicate allocation pressure and cause latency.
process.runtime.dotnet.exceptions.countruntime instrumentationA rising exception rate, even handled ones, is expensive and often hides a real fault.
db.client.operation.durationdata instrumentationQuery latency; if it dominates a request the fix is in the query or pool.

What to put on a dashboard

A .NET dashboard should separate thread-pool starvation from GC pressure from a slow dependency. Row one: endpoint p95/p99 and 5xx by route. Row two: thread-pool queue length and available threads, and GC heap size by generation with collection frequency. Row three: database latency and exception rate.

# p99 latency by endpoint
histogram_quantile(0.99, http.server.request.duration) by (http.route)

# thread-pool starvation: queue length climbing
process.runtime.dotnet.thread_pool.queue.length

# gen-2 heap after GC: a rising floor = leak
process.runtime.dotnet.gc.heap.size{generation="gen2"}

Reading the signals: common failure modes

Thread-pool starvation

The service becomes slow and unresponsive while CPU and memory look healthy, because blocking calls on async paths have exhausted the thread pool and new work queues. Thread-pool queue length climbing while available threads drop is the signature. Fix the sync-over-async call rather than raising the thread-pool limit.

GC pressure

Latency spikes line up with gen-2 collections because the service allocates heavily. Collection frequency and pause behaviour confirm it. Reduce large or frequent allocations on the hot path (pooling, spans, avoiding LOH allocations).

Gen-2 heap leak

Gen-2 heap size grows over hours and never returns to baseline after collection, ending in high memory and eventual failure. Static caches and undisposed resources are the usual causes; the heap-size trend is the tell.

Example alert conditions

Starting thresholds to tune:

ConditionSignalMeaning
5xx rate by route > 2% for 5merrorsEndpoint failing.
thread-pool queue length > 0 sustained for 2mruntimeThread-pool starvation.
gen-2 heap trending up over 24hmemoryProbable leak.
exception rate risingerrorsA fault is generating exceptions.

From monitoring to autonomous resolution

Ops Singularity's TelemetryOps ingests .NET OpenTelemetry data natively, and Sentinel AI resolves the incidents it reveals through governed Action Tickets. Explore TelemetryOps.

Frequently asked questions

How do I add OpenTelemetry to an ASP.NET Core app?

Add the OpenTelemetry packages, configure AddOpenTelemetry() with ASP.NET Core and HttpClient instrumentation, and add an OTLP exporter to your Collector. Alternatively use the automatic instrumentation agent for zero-code setup.

Does .NET have native OpenTelemetry support?

Yes. .NET integrates OpenTelemetry with its built-in Activity tracing and metrics APIs, and provides instrumentation packages plus an automatic instrumentation agent.

See autonomous operations on your own stack.

Bring a real incident. We will show you Sentinel investigate, act and verify end to end, with every action reversible and audited.

Request a Demo → See TelemetryOps