Modern applications generate thousands of requests every minute. Without proper monitoring, identifying performance bottlenecks, failed dependencies, or resource exhaustion becomes difficult. Traditional logging helps explain what happened, but it doesn't provide a real-time view of application health.
OpenTelemetry has become the standard for collecting telemetry across applications. It enables developers to capture metrics, traces, and logs using a vendor-neutral API that integrates with popular observability platforms.
In this article, you'll learn how to implement production-ready OpenTelemetry metrics in a .NET 11 application, expose metrics for monitoring, create custom application metrics, and follow best practices for production deployments.
Note: This article focuses on implementation. Dashboard configurations, alert thresholds, and performance measurements should be validated within your own monitoring environment.
What Are OpenTelemetry Metrics?
Metrics are numerical measurements collected over time that describe the health and performance of an application.
Examples include:
HTTP request count
Request duration
CPU usage
Memory usage
Database query duration
Cache hit ratio
Active users
Background job execution time
Unlike logs, metrics are lightweight and ideal for continuous monitoring.
Why Use OpenTelemetry?
OpenTelemetry provides a consistent way to collect telemetry regardless of the monitoring platform.
Benefits include:
Vendor-neutral instrumentation
Standardized metrics collection
Built-in ASP.NET Core instrumentation
Integration with Prometheus, Grafana, Azure Monitor, and other observability platforms
Support for custom business metrics
Project Setup
Create a new Web API.
dotnet new webapi -n OpenTelemetryMetricsDemo
Install the required packages.
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
Depending on your monitoring stack, you may also install exporters such as Prometheus or Azure Monitor.
Configure OpenTelemetry
Register OpenTelemetry during application startup.
using OpenTelemetry.Metrics;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation()
.AddMeter("ProductApi")
.AddOtlpExporter();
});
var app = builder.Build();
app.Run();
Why This Configuration?
Each component serves a specific purpose:
AddAspNetCoreInstrumentation()captures incoming HTTP request metrics.AddHttpClientInstrumentation()records outgoing HTTP calls.AddRuntimeInstrumentation()collects runtime metrics such as garbage collection and thread pool usage.AddMeter()enables custom application metrics.AddOtlpExporter()sends telemetry to an OpenTelemetry-compatible backend.
Creating Custom Metrics
Business metrics are often more valuable than infrastructure metrics.
Create a custom meter.
using System.Diagnostics.Metrics;
public static class MetricsRegistry
{
public static readonly Meter Meter =
new("ProductApi");
public static readonly Counter<int> OrdersCreated =
Meter.CreateCounter<int>("orders.created");
}
Increment the counter whenever an order is created.
public async Task CreateOrderAsync(Order order)
{
// Save order
MetricsRegistry.OrdersCreated.Add(1);
await Task.CompletedTask;
}
This metric can later be visualized on dashboards to monitor application activity.
Measuring Request Duration
Histograms are useful for recording durations.
public static readonly Histogram<double> ProcessingTime =
MetricsRegistry.Meter.CreateHistogram<double>(
"order.processing.time",
unit: "ms");
Record elapsed time.
var stopwatch = Stopwatch.StartNew();
// Business logic
stopwatch.Stop();
MetricsRegistry.ProcessingTime.Record(
stopwatch.Elapsed.TotalMilliseconds);
Histograms allow monitoring platforms to calculate averages, percentiles, and latency distributions.
Recording Active Users
Observable gauges report values collected during metric scraping.
public static int ActiveUsers;
MetricsRegistry.Meter.CreateObservableGauge(
"users.active",
() => ActiveUsers);
The monitoring system reads the latest value whenever metrics are collected.
End-to-End Flow
A typical request follows this sequence:
Client sends an HTTP request.
ASP.NET Core instrumentation records request metrics.
Business logic updates custom counters and histograms.
Runtime instrumentation collects memory and thread metrics.
Metrics are exported through the configured OTLP exporter.
Dashboards visualize the collected data.
This provides a complete view of both application and infrastructure health.
Useful Metrics to Collect
| Category | Example Metrics |
|---|---|
| HTTP | Request count, duration, status codes |
| Runtime | GC collections, memory usage, thread pool |
| Database | Query duration, failed queries |
| Cache | Hit ratio, misses |
| Business | Orders, registrations, payments |
| External APIs | Latency, failures |
Collecting both technical and business metrics provides better operational visibility.
Exporting Metrics
OpenTelemetry supports multiple exporters.
| Exporter | Typical Use Case |
|---|---|
| OTLP | OpenTelemetry Collector |
| Prometheus | Prometheus + Grafana |
| Azure Monitor | Azure-hosted applications |
| Jaeger | Primarily tracing |
| Console | Local development |
Choose the exporter that aligns with your observability platform.
Production Considerations
Minimize Metric Cardinality
Avoid labels with highly variable values.
Avoid:
userId = 123456
Prefer:
region = us-east
High-cardinality labels increase storage requirements and reduce query performance.
Record Meaningful Metrics
Not every event needs to become a metric.
Focus on metrics that help answer operational questions such as:
Are requests slowing down?
Are database failures increasing?
Is memory usage growing?
Are customers completing purchases?
Keep Metric Names Consistent
Use a clear naming convention.
Examples:
orders.created
orders.failed
orders.processing.time
Consistent names improve dashboard readability and simplify alert configuration.
Monitoring Methodology
The research brief does not include dashboard screenshots, alert thresholds, or production measurements. To evaluate your monitoring implementation:
Test Environment
Keep the following consistent:
.NET SDK version
Application configuration
Monitoring backend
Exporter configuration
Hardware resources
Simulate Traffic
Generate realistic workloads using tools such as:
k6
Apache JMeter
Bombardier
Exercise both successful and failing requests.
Validate Metrics
Verify that dashboards correctly display:
Request throughput
Latency
Error rate
Runtime metrics
Custom business metrics
Also ensure metrics remain accurate under sustained load.
Best Practices
Instrument applications early in development.
Capture both technical and business metrics.
Use meaningful metric names.
Keep labels consistent and low in cardinality.
Monitor runtime metrics alongside application metrics.
Secure metric endpoints where appropriate.
Regularly review dashboards and alert rules.
Test observability during load testing.
Common Mistakes
| Mistake | Impact |
|---|---|
| Recording too many metrics | Increased storage costs |
| High-cardinality labels | Poor query performance |
| Ignoring runtime metrics | Missed infrastructure issues |
| Inconsistent metric names | Confusing dashboards |
| Collecting metrics without alerts | Delayed issue detection |
| Exposing sensitive data in labels | Security risks |
Troubleshooting
Metrics Are Not Appearing
Verify:
Exporter configuration
Collector availability
Network connectivity
Meter registration
Monitoring backend settings
Custom Metrics Are Missing
Ensure:
The meter name matches the registered meter.
The metric is updated during execution.
The exporter is enabled.
High Resource Usage
Review:
Number of collected metrics
Label cardinality
Export interval
Exporter configuration
Reducing unnecessary metrics can improve performance.
FAQs
What is OpenTelemetry?
OpenTelemetry is an open standard for collecting metrics, traces, and logs across applications and services.
What is the difference between metrics and logs?
Metrics provide aggregated numerical measurements over time, while logs capture detailed event information for troubleshooting.
Should I collect every possible metric?
No. Focus on metrics that provide operational value and support monitoring, troubleshooting, and business insights.
Can I create my own metrics?
Yes. OpenTelemetry allows custom counters, histograms, gauges, and other metric instruments using the Meter API.
Do I need Prometheus?
No. OpenTelemetry supports multiple exporters. Prometheus is one popular option, but you can export metrics to any compatible observability platform.
Conclusion
OpenTelemetry provides a standardized and extensible way to monitor .NET applications through metrics. By combining built-in ASP.NET Core instrumentation with custom business metrics, developers gain valuable insights into application performance, reliability, and usage patterns.
A production-ready monitoring strategy goes beyond collecting infrastructure data. It includes meaningful business metrics, consistent naming, controlled metric cardinality, and integration with dashboards and alerting systems. Validate your telemetry pipeline under production-like workloads to ensure it delivers accurate and actionable insights when they matter most.

Join the conversation! Your thoughts help the community grow.