OpenTelemetry Metrics API for .NET
This document teaches how to use the OpenTelemetry Metrics .NET API. To learn how to install and configure the OpenTelemetry .NET SDK, see Getting started with OpenTelemetry .NET.
If you are not familiar with metrics terminology like timeseries or additive/synchronous/asynchronous instruments, read the introduction to OpenTelemetry Metrics first.
Getting started
To get started with metrics, you need to create a Meter using the .NET System.Diagnostics.Metrics API:
using System.Diagnostics.Metrics;
// Create a meter - should be a static/class variable
private static readonly Meter MyMeter = new Meter("MyCompany.MyApp", "1.0.0");
Using the meter, you can create instruments to measure performance. The simplest Counter instrument looks like this:
var requestCounter = MyMeter.CreateCounter<int>("http_requests_total");
// Increment the counter with tags
requestCounter.Add(1,
new KeyValuePair<string, object?>("method", "GET"),
new KeyValuePair<string, object?>("status", "200"));
Counter
Counter is a synchronous instrument that measures additive non-decreasing values, such as requests served, tasks completed, or errors occurred.
// Create a counter for HTTP requests
var httpRequestsCounter = MyMeter.CreateCounter<int>(
name: "http_requests_total",
unit: "1",
description: "Total number of HTTP requests"
);
// Usage in an HTTP handler
public async Task<IActionResult> GetUsers()
{
try
{
var users = await userService.GetUsersAsync();
// Count successful requests
httpRequestsCounter.Add(1,
new KeyValuePair<string, object?>("method", "GET"),
new KeyValuePair<string, object?>("status", "200"));
return Ok(users);
}
catch (Exception ex)
{
// Count failed requests
httpRequestsCounter.Add(1,
new KeyValuePair<string, object?>("method", "GET"),
new KeyValuePair<string, object?>("status", "500"));
throw;
}
}
UpDownCounter
UpDownCounter is a synchronous instrument that measures values that can go up or down, such as active connections, queue size, or memory usage.
// Create an up-down counter for active connections
var activeConnectionsCounter = MyMeter.CreateUpDownCounter<int>(
name: "db_connections_active",
unit: "1",
description: "Number of active database connections"
);
// Usage in connection management
public class ConnectionPool
{
public IDbConnection GetConnection()
{
var connection = CreateConnection();
// Increment when connection is created
activeConnectionsCounter.Add(1);
return connection;
}
public void ReleaseConnection(IDbConnection connection)
{
connection.Close();
// Decrement when connection is released
activeConnectionsCounter.Add(-1);
}
}
Histogram
Histogram is a synchronous instrument that records a distribution of values, perfect for measuring request durations, response sizes, or query execution times.
// Create a histogram for request duration
var requestDurationHistogram = MyMeter.CreateHistogram<double>(
name: "http_request_duration_seconds",
unit: "s",
description: "Duration of HTTP requests in seconds"
);
// Usage with Stopwatch
public async Task<IActionResult> GetUsers()
{
var stopwatch = Stopwatch.StartNew();
try
{
var users = await userService.GetUsersAsync();
return Ok(users);
}
finally
{
stopwatch.Stop();
// Record the duration
requestDurationHistogram.Record(stopwatch.Elapsed.TotalSeconds,
new KeyValuePair<string, object?>("method", "GET"),
new KeyValuePair<string, object?>("endpoint", "/api/users"));
}
}
Gauge
Gauge is a synchronous instrument that records the current value of something at the time of measurement.
// Create a gauge for temperature
var temperatureGauge = MyMeter.CreateGauge<double>(
name: "room_temperature_celsius",
unit: "°C",
description: "Current room temperature"
);
// Usage when temperature changes
public void OnTemperatureChanged(double newTemperature)
{
// Record the current temperature
temperatureGauge.Record(newTemperature,
new KeyValuePair<string, object?>("room", "server_room"));
}
ObservableCounter
ObservableCounter is an asynchronous instrument that measures additive non-decreasing values that are collected via a callback.
// Create an observable counter for total processed orders
MyMeter.CreateObservableCounter<int>(
"orders_total",
() => GetTotalOrdersFromDatabase(),
"1",
"Total number of processed orders"
);
private int GetTotalOrdersFromDatabase()
{
// Query your database or service to get the current total
return orderService.GetTotalOrderCount();
}
ObservableUpDownCounter
ObservableUpDownCounter is an asynchronous instrument that measures additive values that can increase or decrease.
// Create observable up-down counter for queue size
MyMeter.CreateObservableUpDownCounter<int>(
"queue_size",
() => messageQueue.Count,
"1",
"Current number of messages in queue"
);
ObservableGauge
ObservableGauge is an asynchronous instrument that measures non-additive values that can go up and down.
// Create an observable gauge for memory usage
MyMeter.CreateObservableGauge<long>(
"process_memory_bytes",
() => GC.GetTotalMemory(forceFullCollection: false),
"By",
"Current memory usage of the process"
);
// For multiple measurements with attributes
private static IEnumerable<Measurement<double>> GetCpuMeasurements()
{
yield return new Measurement<double>(GetUserCpuUsagePercent(),
new KeyValuePair<string, object?>("type", "user"));
yield return new Measurement<double>(GetSystemCpuUsagePercent(),
new KeyValuePair<string, object?>("type", "system"));
}
MyMeter.CreateObservableGauge<double>(
"system_cpu_usage",
GetCpuMeasurements,
"%",
"CPU usage percentage"
);
Dependency Injection
In ASP.NET Core applications, use IMeterFactory for proper dependency injection:
public class UserMetrics
{
private readonly Counter<int> _userCreatedCounter;
private readonly Histogram<double> _userQueryDuration;
public UserMetrics(IMeterFactory meterFactory)
{
var meter = meterFactory.Create("MyApp.UserService");
_userCreatedCounter = meter.CreateCounter<int>(
"users_created_total",
description: "Total number of users created"
);
_userQueryDuration = meter.CreateHistogram<double>(
"user_query_duration_seconds",
unit: "s",
description: "Duration of user database queries"
);
}
public void RecordUserCreated()
{
_userCreatedCounter.Add(1);
}
public void RecordQueryDuration(double seconds)
{
_userQueryDuration.Record(seconds);
}
}
// Register in Program.cs
builder.Services.AddSingleton<UserMetrics>();
OpenTelemetry APM
Uptrace is a DataDog competitor that supports distributed tracing, metrics, and logs. You can use it to monitor applications and troubleshoot issues.

Uptrace comes with an intuitive query builder, rich dashboards, alerting rules with notifications, and integrations for most languages and frameworks.
Uptrace can process billions of spans and metrics on a single server and allows you to monitor your applications at 10x lower cost.
In just a few minutes, you can try Uptrace by visiting the cloud demo (no login required) or running it locally with Docker. The source code is available on GitHub.