Enterprise Application, .NET Development 07 October 2026

How Does .NET OpenTelemetry Work in Production?

Key Takeaways:

  • .NET OpenTelemetry works well with built-in .NET primitives such as Activity, Meter, and ILogger, which reduces the need for custom instrumentation.
  • Auto-instrumentation should come first, followed by correlation, database attribution, and business metrics as the observability setup matures.
  • .NET distributed tracing becomes more useful when trace and span identifiers flow consistently across services, logs, and message queues.
  • Cardinality limits and sampling rules should be defined before rollout to keep telemetry storage and observability costs under control.
  • Tail sampling helps retain errors and slow requests while reducing unnecessary trace volume in production environments.
  • OpenTelemetry instrumentation should focus on meaningful operations instead of adding spans around every method, which creates noise and additional cost.

Introduction

A single IIS-hosted application with an event log was once manageable because teams could tail the log and trace issues directly. A containerized service calling several others behind a load balancer is far harder to diagnose with that approach. The diagnostic surface has changed, which is why .NET distributed tracing now plays a central role in production troubleshooting.

Modern .NET makes .NET OpenTelemetry practical because its core observability primitives already exist in the base class library. Activity, Meter, and ILogger already align with OpenTelemetry in .NET, so many teams need configuration more than new instrumentation code. In practice, teams completing a .NET Framework to .NET 10 Migration often have more telemetry available than they realize and are missing only an exporter.

This guide focuses on what to instrument first, how to order the rollout, and how to control telemetry cost. It also explains the mistakes that can make .NET observability expensive, disconnected, and less useful during real production incidents.

Why choose OpenTelemetry in .NET over a vendor SDK?

OpenTelemetry gives .NET teams a standards-based observability layer without tying instrumentation decisions to a single backend provider or platform. Three practical reasons, none of them ideological.

  • The instrumentation is already in .NET. System.Diagnostics.Activity is the tracing primitive, while System.Diagnostics.Metrics.Meter handles metrics. Both map directly to OpenTelemetry concepts. Microsoft’s own libraries, including ASP.NET Core, HttpClient, and Entity Framework Core, emit through these primitives. Adopting OpenTelemetry in .NET mostly means configuring an exporter for signals that already exist.
  • Backend portability. Write instrumentation once and export it anywhere. Changing observability vendors becomes a configuration change rather than a re-instrumentation project. This also improves your position at renewal.
  • One correlation model across signals. Traces, metrics, and logs share trace and span identifiers. This makes moving from a latency spike to the specific slow request far easier.

.NET Observability With OpenTelemetry
Build upward. Correlation is the low-cost layer that makes everything above it more useful.

Layer 1: auto-instrumentation

Registering the ASP.NET Core, HttpClient, and Entity Framework Core instrumentation packages creates incoming request, outgoing call, and database spans. No application code changes are required. Runtime metrics, including garbage collection, thread pool activity, and exceptions, come from the same registration.

For most services, this level of OpenTelemetry instrumentation already answers a large share of routine questions.

Layer 2: correlation

This is the layer teams skip most often, and it is why many observability deployments disappoint.

Trace context must propagate across service boundaries through W3C traceparent headers. The .NET HttpClient instrumentation handles this automatically. Context must also flow into logs, so every log line carries the trace and span identifier.

Without that, a slow trace and the exception explaining it sit in different tools with no reliable way to join them. With correlation, moving between the two takes a click.

Message queues need explicit attention because context does not propagate through a broker automatically. It must be written into message headers and read on the other side.

Layer 3: custom spans and database attribution

Auto-instrumentation can show that a database call took 400 milliseconds. It cannot show that the same query ran 340 times in one request.

Attributing queries to the calling operation is the highest-value custom instrumentation available. It makes the N+1 pattern visible in production instead of only in a local SQL log. That pattern slows more Entity Framework Core endpoints than any other cause, as covered in ASP.NET Core performance tuning.

Add custom spans around meaningful business operations such as order submission, document generation, and external settlement. Avoid adding spans around every method.

Layer 4: business metrics

Technical health is not the same as service health. A service returning HTTP 200 while processing zero orders is still broken. OpenTelemetry metrics such as counters and histograms for domain events answer questions that CPU dashboards never will.

Blog Book a Consultation Blog

When should you hire experts for .NET observability?

If you have no tracing in place, register the auto-instrumentation packages and connect them to a backend. This usually takes an afternoon and delivers most of the value. You do not need an observability engagement for that setup.

If the plan is to Hire .Net Developer support for a full custom-span rollout across every service, push back. Start with auto-instrumentation and trace IDs in logs. Run it for a month, then add OpenTelemetry instrumentation where existing telemetry cannot answer your questions. Instrumenting everything upfront increases cost and makes useful signals harder to identify.

If the requirement is limited to dashboards for a status page, that is a different and less expensive solution than what is described here.

How can .NET OpenTelemetry control telemetry costs?

Observability bills can grow faster than traffic when no one owns them, and two factors account for most of that growth.

  • Cardinality. Every unique combination of metric attribute values creates a separate time series. Add a user identifier to a request counter, and you create one series per user. That is how a modest service can produce millions of series. Attributes should use bounded values: route templates instead of raw URLs, status classes instead of exact statuses, and regions instead of instances.
  • Sampling. Storing every trace is unnecessary and expensive. There are two common approaches, with a clear default.

ApproachHow it decidesTrade-off
Head samplingAt the start of the trace, by percentageCheap and simple; discards errors at the same rate as successful traces
Tail samplingAfter the trace completes, based on outcomeKeeps errors and slow traces; requires a collector to hold spans in memory

Tail sampling is the better default for production because random sampling can discard the traces teams need most. A common configuration keeps 100% of errors, 100% of traces above a latency threshold, and a small percentage of the rest.

Deploy the OpenTelemetry Collector as a sidecar or daemon instead of exporting directly from each application. The collector centralizes sampling, attribute filtering, and redaction. Changes can then be handled through configuration rather than an application redeploy.

What can go wrong with OpenTelemetry instrumentation in production?

Production observability often fails because small implementation choices weaken correlation, increase telemetry costs, and make incidents harder to diagnose. Five recurring mistakes, all avoidable.

  • Logs without trace identifiers. This is the most common and most damaging mistake. Without trace identifiers, OpenTelemetry logging can leave three tools telling separate stories about the same request.
  • Unbounded attribute cardinality. This problem is often discovered through the bill. Enforce an allowlist of attribute keys in the collector instead of relying on every service to behave correctly.
  • Instrumenting everything at once. Adding a span around every method creates noise that hides useful signals and increases storage costs. Start with auto-instrumentation, then add OpenTelemetry instrumentation deliberately.
  • Personal data in spans. Request bodies, query parameters, and headers often contain personal or payment data. Telemetry backends are rarely covered by the controls that this data requires. Redact sensitive data at the collector and treat this as a design decision, not a cleanup task. The same reasoning applies to SOC 2 and HIPAA readiness.
  • Alerting on causes rather than symptoms. CPU and memory alerts often create noise. Alerts based on user-visible symptoms such as error rate, latency percentile, and queue age are more actionable. Traces can then explain the underlying cause.

One .NET-specific point also matters. Activity has been part of the base class library for years. Libraries emitting through it produce spans automatically once a listener is registered. Teams often find that the missing piece was simply an exporter. For containerized services, relevant deployment considerations after a runtime change are covered in running .NET on Linux and containers.

Why is correlation essential for OpenTelemetry tracing?

Layer 2 is correlation, and it is often the first layer teams skip during an observability rollout.

When a .NET observability engagement is scoped, the most visible deliverables usually receive the most attention. Dashboards, custom spans around business operations, and service maps are easy to evaluate. Correlation is different because it works in the background and produces no standalone artifact that demonstrates its value. As a result, it can become an easy line item to defer when budgets are reduced.

The impact appears later. Teams may have OpenTelemetry tracing in one tool and OpenTelemetry logging in another, but no reliable way to connect them. During an incident, engineers then have to match timestamps manually to understand what happened. That is exactly the problem observability is meant to prevent.

Layers 3 and 4 should therefore not be implemented unless correlation is included in the same phase. This is not about adding scope unnecessarily. Custom spans and business metrics depend on connected telemetry to provide useful diagnostic context. Without correlation, the later layers may exist technically but cannot deliver their intended value.

Treating correlation as a core requirement avoids that gap. Experience from production environments shows that delaying it often creates more operational work later and reduces the value of the entire observability setup.

Conclusion

A reliable .NET OpenTelemetry setup depends on getting the instrumentation order right from the start. Begin with auto-instrumentation because most of the required signals already exist in modern .NET. Add correlation next so trace identifiers connect logs with the requests they belong to.

Database attribution should follow because it exposes repeated queries and N+1 patterns in production. Business metrics come later, once technical signals can be tied to real service outcomes. Cost control also needs early planning across .NET development services. Set cardinality limits and choose a sampling strategy before telemetry volumes grow.

Tail sampling through a collector preserves errors and slow requests while reducing unnecessary trace storage. This makes .NET observability more useful without letting costs rise unchecked. Finally, alert on user-visible symptoms and use OpenTelemetry tracing to find the cause. Dashboards can show that something is wrong, but traces explain where the failure started and why it happened.

Blog Book a Consultation Blog

FAQs

Not for the primitives. System.Diagnostics.Activity and Meter are already OpenTelemetry-compatible, and Microsoft's own libraries emit through them. Adoption mainly means registering instrumentation packages and configuring an exporter.
Metrics answer whether something is wrong, in aggregate and cheaply. Traces answer why a specific request was slow or failed. Logs supply detail at a point in time. All three become far more valuable when correlated by shared trace identifiers.
The library is free, and storage and ingest aren't. Cost is driven by metric cardinality and trace volume, so bounded attribute values and tail sampling through a collector keep both predictable. Decide both before rollout rather than after the first invoice.
Author
Author

Bhavesh Kachadiya

Bhavesh Kachadiya is a Full Stack Engineer and Technical Manager at CodeTrade with over a decade of experience building web applications from the ground up. He works across the entire stack, leads engineering teams, and has a reputation for turning complex technical requirements into clean, working software.