.NET 8 API with Prometheus and Grafana

Fauly Coelho
4 min readMar 11, 2024

.NET 8 API with Prometheus and Grafana

Dear reader, Prometheus and Grafana are open-source monitoring and visualization tools commonly used together to collect, store, and display metrics and logs from various systems and applications. By integrating the Asp.net API with Prometheus, you can gather data, while Grafana provides a user-friendly interface to create dashboards and analyze the collected information. In this article we are going to talk about how to implement telemetry in Asp.net API using those tools.

Read more about: Prometheus and Grafana

Requirements:

Before you begin, make sure you have the following software installed on your machine

Visual Studio Code

.NET 8 SDK

Docker

Create a new WebApi project and install the Prometheus package on it:

dotnet new webapi -n my-api -minimal 
cd my-api
dotnet add package prometheus-net.AspNetCore
dotnet restore

Let’s do some changes to our code, using the Visual Studio Code, edit the Program.cs

You can remove unnecessary code related to ‘WeatherForecast’. Add the following line:

using Prometheus;
[...]
builder.Services.UseHttpClientMetrics();
[...]
app.UseMetricServer();
app.UseHttpMetrics();

Create a new endpoint:

app.MapGet("/random-number", () =>
{
var number = Random.Shared.Next(0, 10);
if (number >= 7)
return Results.Unauthorized();
return Results.Ok(number);
});

in the end, it will look like this:

using Prometheus;
var builder = WebApplication.CreateBuilder(args);builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.UseHttpClientMetrics();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseMetricServer();
app.UseHttpMetrics();
app.MapGet("/random-number", () =>
{
var number = Random.Shared.Next(0, 10);
if (number >= 7)
return Results.Unauthorized();
return Results.Ok(number);
});
app.Run();

In the ‘./Properties/launchSettings.json’ file, add the “Container (Dockerfile)” node, it will look like this:

{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:32818",
"sslPort": 44375
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5013",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7035;http://localhost:5013",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTP_PORTS": "8080"
},
"publishAllPorts": true,
"useSSL": true
}
}
}

Inside “my-api” folder, add a new Dockerfile and past this code:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["my-api/my-api.csproj", "my-api/"]
RUN dotnet restore "./my-api/my-api.csproj"
COPY . .
WORKDIR "/src/my-api"
RUN dotnet build "./my-api.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./my-api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "my-api.dll"]

Outside of ‘my-api’ folder, create a file called ‘prometheus.yml’ and past it:

global:
scrape_interval: 10s
scrape_configs:
- job_name: 'my-api-read-prometheus'
static_configs:
- targets: ['my-api:8080']

And create a file called ‘docker-compose.yml’ and past it:

version: '3.4'
services:
my-api:
image: ${DOCKER_REGISTRY-}my-api
ports:
- 8080:8080
build:
context: .
dockerfile: my-api/Dockerfile
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_HTTP_PORTS=8080
networks:
- mynw
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
command:
- '--config.file=/etc/prometheus/prometheus.yml'
networks:
- mynw
grafana:
image: grafana/grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- ./grafana:/var/lib/grafana
depends_on:
- prometheus
networks:
- mynw
networks:
mynw:

All configuration were done, let’s test our project.

Run the containers:

docker compose build
docker compose up

Wait until it finish, and now you can access your API, sometime you will get an HTTP 200 with a number, and sometimes a error page with 401 unauthorized.

API: http://localhost:8080/random-number

You also can see metrics from your API in the ‘/metrics ‘ endpoint:

API Metrics: http://localhost:8080/metrics

Check if your Prometheus container is connected to your API:

http://localhost:9090/targets

Expected result:

Go to Grafana: http://localhost:3000/

Log in with username and password ‘admin’, after that, and add a new data source connection:

Click on ‘Add new Data Source’ in the ‘Connection’ set the Prometheus URL: “http://prometheus:9090” (this is our container name)

Go to Dashboards > Create new Dash board> + Add Visualization

Code, add: “sum by(code) (http_request_duration_seconds_bucket)

It is done, now you can research more about other metrics and enrich your app and dashboard.

As a Brief conclusion

Benefits of using Prometheus include the ability to understand where your app is receiving the most hits, providing clear insights into traffic and performance

--

--