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

  1. Integrate OpenTelemetry: Automatically collect HTTP metrics such as request counts and response durations from your .NET application.
  2. Expose Metrics to Prometheus: Make metrics available via a /metrics endpoint for Prometheus scraping.
  3. Add Custom Metrics: Use a custom meter to track application-specific metrics, like total HTTP requests.
  4. Simulate a Dice Roll: Implement a /rolldice/{player?} endpoint that logs and returns the result of a dice roll.
  5. Analyze Metrics: Use Prometheus for querying, visualizing, and alerting on application metrics.

Prerequisites

Before you start, ensure you have the following:

  1. AWS Account with an Ubuntu 24.04 LTS EC2 Instance.
  2. .NET SDK installed on your system.

Helpful References

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 named RollDiceApp to store your project files.
  • cd RollDiceApp: Changes your working directory to the newly created RollDiceApp, 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 like Program.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?
  1. using OpenTelemetry.Instrumentation.AspNetCore:
    • Adds automatic instrumentation for incoming HTTP requests handled by your ASP.NET Core application.
  2. using OpenTelemetry.Instrumentation.Http:
    • Tracks outgoing HTTP requests made by the application, such as API calls to external services.
  3. using Prometheus:
    • Enables the /metrics endpoint, allowing Prometheus to scrape and collect metrics for monitoring.
  4. 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
  1. 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();
  2. 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");
  3. 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
  1. 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"))
  2. 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()
  3. 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()
  4. 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();
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
  1. Routing Middleware:
    • Purpose: Prepares the application to handle route-based requests.Why?: Required for mapping controllers and endpoints like /metrics.
    app.UseRouting();
  2. 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
  3. Map Controllers:
    • Purpose: Maps API endpoints defined in your controllers to the application.Why?: Ensures that API routes like /rolldice are accessible.
    app.MapControllers();
  4. 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
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:

  1. Enables metrics collection at the middleware level using HttpMetrics.
  2. Ensures that the /metrics endpoint is accessible for Prometheus to scrape and monitor your application.
  3. 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

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:

  1. Integrate Prometheus with your application by configuring it to scrape the /metrics endpoint.
  2. Add Grafana to visualize the metrics collected by Prometheus.
  3. 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

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *