Monitoring the performance of your .NET application is essential to ensure reliability and maintain optimal performance. By collecting HTTP metrics, you can gain valuable insights into how your application handles requests, including response times, request counts, and error rates.
In this blog, we’ll build upon a dice-rolling application (RollDiceApp
) that integrates OpenTelemetry and Prometheus to collect and expose HTTP metrics, while showcasing how to add custom metrics for even greater insights.
Why Choose OpenTelemetry and Prometheus?
Combining OpenTelemetry and Prometheus creates a reliable observability stack for modern applications. OpenTelemetry collects telemetry data in a standardized way, while Prometheus provides tools to analyze and visualize these metrics. Together, they enable:
- Seamless Integration: Automatically collect and expose metrics.
- Real-Time Insights: Track application performance metrics live.
- Scalability: Handle telemetry data for applications of any size.
- Flexibility: Add custom metrics and integrate with tools like Grafana.
This integration empowers developers to monitor application health, resolve issues, and optimize performance effectively.
How It Works
- Integrate OpenTelemetry: Automatically collect HTTP metrics such as request counts and response durations from your .NET application.
- Expose Metrics to Prometheus: Make metrics available via a
/metrics
endpoint for Prometheus scraping. - Add Custom Metrics: Use a custom meter to track application-specific metrics, like total HTTP requests.
- Simulate a Dice Roll: Implement a
/rolldice/{player?}
endpoint that logs and returns the result of a dice roll. - Analyze Metrics: Use Prometheus for querying, visualizing, and alerting on application metrics.
Prerequisites
Before you start, ensure you have the following:
- AWS Account with an Ubuntu 24.04 LTS EC2 Instance.
- .NET SDK installed on your system.
Helpful References
- Install .NET 8.0 SDK and .NET Runtime on Ubuntu 24.04 LTS
- How to Create a Simple .NET Application (RollDice) with an HTTP Server
- Integrate OpenTelemetry for .NET Application
Step 1: Set Up the Environment
Create a new directory for your application and navigate into it:
sudo mkdir RollDiceApp
cd RollDiceApp
Explanation:
sudo mkdir RollDiceApp
: Creates a new directory namedRollDiceApp
to store your project files.cd RollDiceApp
: Changes your working directory to the newly createdRollDiceApp
, preparing for subsequent commands.
Step 2: Generate a .NET Web App
Generate a new .NET web application using the following command:
sudo dotnet new web
Explanation:
dotnet new web
: Creates a minimal .NET web application template. This includes files likeProgram.cs
and a basic setup for building and running an HTTP server.
Step 3: Update Application for HTTP Metrics Collection
In the previous setup, we created a simple dice-rolling .NET application (RollDiceApp
) and integrated OpenTelemetry for tracing and logging. To further collect HTTP Metrics using OpenTelemetry and expose them to Prometheus, additional configurations and packages are needed.
Add Required Using Directives
Open your Program.cs file and ensure the following using
directives are added:
Existing Configurations
These were already included in the previous blog for integrating OpenTelemetry with .NET:
using System.Globalization;
using Microsoft.AspNetCore.Mvc;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
Additional Configurations for Metrics Collection
Add these new using
directives to extend the application’s capabilities:
using OpenTelemetry.Instrumentation.AspNetCore; // Automatic instrumentation for HTTP requests.
using OpenTelemetry.Instrumentation.Http; // Tracks outgoing HTTP requests.
using Prometheus; // Exposes metrics for Prometheus scraping.
using System.Diagnostics.Metrics; // Creates custom metrics for detailed monitoring.
Why These Changes?
using OpenTelemetry.Instrumentation.AspNetCore
:- Adds automatic instrumentation for incoming HTTP requests handled by your ASP.NET Core application.
using OpenTelemetry.Instrumentation.Http
:- Tracks outgoing HTTP requests made by the application, such as API calls to external services.
using Prometheus
:- Enables the
/metrics
endpoint, allowing Prometheus to scrape and collect metrics for monitoring.
- Enables the
using System.Diagnostics.Metrics
:- Supports the creation of custom metrics to track specific application behaviors or KPIs.
Step 4: Add Services and Custom Metrics to the Container
To enable HTTP request handling and track application-specific metrics, you need to add services and define a custom meter for metrics collection. Update your Program.cs file with the following configurations.
Where to Add the Code
Place the following code under:
var builder = WebApplication.CreateBuilder(args);
const string serviceName = "roll-dice";
Code to Add
// Add services to the container.
builder.Services.AddControllers();
// Create a custom meter for the API
var meter = new Meter("roll-dice.Metrics", "1.0");
// Define an HTTP request counter
var httpRequestCounter = meter.CreateCounter<long>("http_requests_total", description: "Total number of HTTP requests");
Explanation
- Controller Services:
- Purpose: The
AddControllers()
method registers API controllers to handle incoming HTTP requests.Why?: Enables REST API functionality for defining endpoints, such as/rolldice
.
builder.Services.AddControllers();
- Purpose: The
- Custom Meter:
- Purpose: The
Meter
class creates a namespace for defining and managing custom metrics.Why?: Provides flexibility to create metrics specific to your application (e.g., tracking HTTP requests).
var meter = new Meter("roll-dice.Metrics", "1.0");
- Purpose: The
- HTTP Request Counter:
- Purpose: Tracks the total number of HTTP requests handled by your application.Why?: Offers visibility into API usage and helps identify high-traffic endpoints.
var httpRequestCounter = meter.CreateCounter<long>("http_requests_total", description: "Total number of HTTP requests");
Updated Code Snippet in Context
After adding the code, your Program.cs should look like this:
var builder = WebApplication.CreateBuilder(args);
const string serviceName = "roll-dice";
// Add services to the container.
builder.Services.AddControllers();
// Create a custom meter for the API
var meter = new Meter("roll-dice.Metrics", "1.0");
// Define an HTTP request counter
var httpRequestCounter = meter.CreateCounter<long>("http_requests_total", description:
"Total number of HTTP requests");
Step 5: Configure OpenTelemetry for Metrics Collection
In this step, you will configure OpenTelemetry to automatically collect HTTP metrics for incoming and outgoing requests. This configuration also integrates a Prometheus exporter to expose metrics at the /metrics
endpoint.
Where to Add the Code
Add the following code after the logging and tracing configuration in your Program.cs file:
builder.Services.AddOpenTelemetry()
.WithMetrics(meterProviderBuilder =>
{
meterProviderBuilder
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("roll-dice"))
.AddAspNetCoreInstrumentation() // Tracks incoming HTTP request metrics
.AddHttpClientInstrumentation() // Tracks outgoing HTTP request metrics
.AddPrometheusExporter(); // Expose metrics to Prometheus
});
Explanation
- Resource Builder:
- Purpose: Adds a
service.name
attribute to all metrics to identify your application in monitoring systems.Why?: Helps you group metrics under a specific service name (roll-dice
).
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("roll-dice"))
- Purpose: Adds a
- AspNetCore Instrumentation:
- Purpose: Automatically captures metrics for incoming HTTP requests, such as request count, latency, and status codes.Why?: Simplifies monitoring without requiring manual instrumentation.
.AddAspNetCoreInstrumentation()
- HttpClient Instrumentation:
- Purpose: Tracks metrics for outgoing HTTP requests, such as request duration and response status.Why?: Useful for monitoring external API calls made by your application.
.AddHttpClientInstrumentation()
- Prometheus Exporter:
- Purpose: Exposes metrics in Prometheus-compatible format via the
/metrics
endpoint.Why?: Enables Prometheus to scrape and visualize your application’s metrics.
.AddPrometheusExporter();
- Purpose: Exposes metrics in Prometheus-compatible format via the
Updated Code Snippet in Context
After adding the metrics configuration, the relevant part of your Program.cs file should look like this:
builder.Services.AddOpenTelemetry()
.WithTracing(tracerProviderBuilder =>
{
tracerProviderBuilder
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("roll-dice"))
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddConsoleExporter();
})
.WithMetrics(meterProviderBuilder =>
{
meterProviderBuilder
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("roll-dice"))
.AddAspNetCoreInstrumentation() // Tracks incoming HTTP request metrics
.AddHttpClientInstrumentation() // Tracks outgoing HTTP request metrics
.AddPrometheusExporter(); // Expose metrics to Prometheus
});
Step 6: Add Middleware for Metrics Collection
To enable metrics collection and make them accessible to Prometheus, add the required middleware to your Program.cs file. This ensures that HTTP metrics are tracked, and the /metrics
endpoint is exposed for Prometheus scraping.
Where to Add the Code
Place the following code in the Program.cs file before the app.Run();
line:
Code to Add
app.UseRouting();
app.UseHttpMetrics(); // Middleware for collecting HTTP metrics
app.MapControllers();
app.MapMetrics(); // Expose the /metrics endpoint for Prometheus scraping
Explanation
- Routing Middleware:
- Purpose: Prepares the application to handle route-based requests.Why?: Required for mapping controllers and endpoints like
/metrics
.
app.UseRouting();
- Purpose: Prepares the application to handle route-based requests.Why?: Required for mapping controllers and endpoints like
- HttpMetrics Middleware:
- Purpose: Captures HTTP metrics such as request durations, counts, and response codes.Why?: Provides essential metrics for analyzing the performance of your API.
app.UseHttpMetrics(); // Middleware for collecting HTTP metrics
- Map Controllers:
- Purpose: Maps API endpoints defined in your controllers to the application.Why?: Ensures that API routes like
/rolldice
are accessible.
app.MapControllers();
- Purpose: Maps API endpoints defined in your controllers to the application.Why?: Ensures that API routes like
- Map Metrics:
- Purpose: Exposes the
/metrics
endpoint, allowing Prometheus to scrape metrics.Why?: Essential for making application metrics available to monitoring systems.
app.MapMetrics(); // Expose the /metrics endpoint for Prometheus scraping
- Purpose: Exposes the
Updated Code Snippet in Context
After adding the middleware, your Program.cs file should look like this:
app.UseRouting();
app.UseHttpMetrics(); // Middleware for collecting HTTP metrics
app.MapControllers();
app.MapMetrics(); // Expose the /metrics endpoint for Prometheus scraping
app.Run();
Why Is This Step Important?
This step:
- Enables metrics collection at the middleware level using
HttpMetrics
. - Ensures that the
/metrics
endpoint is accessible for Prometheus to scrape and monitor your application. - Completes the integration of metrics tracking and exposure in your application.
Step7: Update Project Dependencies
To ensure your RollDiceApp project has all the required dependencies for OpenTelemetry, Prometheus, and additional functionality, update the RollDiceApp.csproj
file with the correct dependencies.
Updated RollDiceApp.csproj
File
RollDiceApp.csproj
Open the RollDiceApp.csproj
file and include the following dependencies:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.6.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="prometheus-net.AspNetCore" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>
Explanation of Dependencies
Microsoft.AspNetCore.OpenApi
: Adds OpenAPI (Swagger) support for documenting your APIs.OpenTelemetry.Exporter.Console
: Exports telemetry data to the console for debugging purposes.OpenTelemetry.Exporter.Prometheus.AspNetCore
: Exports metrics to Prometheus via a/metrics
endpoint.OpenTelemetry.Extensions.Hosting
: Simplifies OpenTelemetry configuration for .NET applications.OpenTelemetry.Instrumentation.AspNetCore
: Automatically collects telemetry for incoming HTTP requests.OpenTelemetry.Instrumentation.Http
: Captures telemetry for outgoing HTTP requests.prometheus-net.AspNetCore
: Provides Prometheus middleware to expose metrics in ASP.NET Core.Swashbuckle.AspNetCore
: Adds Swagger support for API documentation
Step8: Build, Run, and Verify the Application
Now that your RollDiceApp project is configured with all necessary dependencies, it’s time to build, run, and verify the application. This step ensures that your project compiles successfully and the metrics and functionality work as expected.
Build the Application
To build your project, run the following command:
sudo dotnet build
Expected Output
If everything is configured correctly, you should see an output similar to this:
Microsoft (R) Build Engine version 17.0.0+154d2fc23 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore...
Restored /path/to/RollDiceApp.csproj (in 1.25 sec).
RollDiceApp -> /path/to/bin/Debug/net8.0/RollDiceApp.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:03.14
If you encounter any errors, review the error messages, especially for missing dependencies or improperly configured XML in the .csproj
file.
Run the Application
To run your application, use the following command:
sudo dotnet run
Expected Output
When you run the application, you should see output indicating that the server is running. For example:
Building...
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: /path/to/RollDiceApp
Test the Application
Access the /metrics
Endpoint
Open your browser or use a tool like curl
to access the /metrics
endpoint exposed by Prometheus:
curl http://localhost:5000/metrics
Expected Output
The /metrics
endpoint should return Prometheus-compatible metrics, including the custom counter (http_requests_total
) and default metrics collected by OpenTelemetry:
# HELP http_requests_total Total number of HTTP requests
# TYPE http_requests_total counter
http_requests_total 5
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.694848e+09
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 0.234
Test the /rolldice
Endpoint
Next Steps
With the application running successfully, you can now:
- Integrate Prometheus with your application by configuring it to scrape the
/metrics
endpoint. - Add Grafana to visualize the metrics collected by Prometheus.
- Extend the application with additional metrics and tracing functionality.
To know what is .NET please click on “.NET“
For any queries contact us: Rushi-Infotech