Tuesday, 10 June 2025

How to add labels to the exposed Prometheus metrics in Java?

In Prometheus, labels are key-value pairs attached to metrics to add more context 

For example

app_total_count_total{endpoint="/health",app_name="my_app",} 3.0
app_total_count_total{endpoint="/user-profile",app_name="my_app",} 7.0

In the above example, we have a single metric name app_total_count_total with two dimensions:

 

·      endpoint

·      app_name

 

These two dimensions endpoint, app_name are the labels for this metric.

 

How to Add Labels in Java?

You can do this when building your Metirc (Ex: Counter):

Counter appRequestCounter = Counter.build()
.name("app_total_count")
.help("Total requests by endpoint and app name")
.labelNames("endpoint", "app_name")
.register();

Then increment with labels:

·      appRequestCounter.labels("/health", "my_app").inc();

Here the label endpoint is set to /health and the app_name is set to my_app

 

·      appRequestCounter.labels("/user-profile", "my_app").inc();

Here the label endpoint is set to /user-profile and the app_name is

set to my_app

 

Let’s build an application and understand the labels use cases better.

 

PrometheusLabelMetricsApp.java

package com.sample.app;

import io.prometheus.client.Counter;
import io.prometheus.client.exporter.common.TextFormat;
import io.prometheus.client.hotspot.DefaultExports;
import io.prometheus.client.CollectorRegistry;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.List;

public class PrometheusLabelMetricsApp {

  // Define counter with both 'endpoint' and 'app_name' labels
  static final Counter appRequestCounter = Counter.build().name("app_total_count")
      .help("Total requests by endpoint and app name").labelNames("endpoint", "app_name").register();

  static final String APP_NAME = "my_app";

  // Supported endpoints
  static final List<String> validEndpoints = Arrays.asList("/health", "/login", "/logout", "/user-profile");

  public static void main(String[] args) throws IOException {
    // Optional: Export default JVM metrics
    // DefaultExports.initialize();

    // Start HTTP server
    HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);

    // Handle /metrics endpoint
    server.createContext("/metrics", new MetricsHandler());

    // Handle your 4 sample endpoints
    for (String endpoint : validEndpoints) {
      server.createContext(endpoint, new AppEndpointHandler(endpoint));
    }

    server.setExecutor(null);
    server.start();

    System.out.println("Server started on http://localhost:8080");
    System.out.println("Metrics available at: http://localhost:8080/metrics");
  }

  // Handler for metrics endpoint
  static class MetricsHandler implements HttpHandler {
    @Override
    public void handle(HttpExchange exchange) throws IOException {
      StringWriter writer = new StringWriter();
      TextFormat.write004(writer, CollectorRegistry.defaultRegistry.metricFamilySamples());

      String response = writer.toString();
      exchange.getResponseHeaders().set("Content-Type", TextFormat.CONTENT_TYPE_004);
      exchange.sendResponseHeaders(200, response.length());

      try (OutputStreamWriter os = new OutputStreamWriter(exchange.getResponseBody())) {
        os.write(response);
      }
    }
  }

  // Handler for simulated API endpoints
  static class AppEndpointHandler implements HttpHandler {
    private final String endpoint;

    AppEndpointHandler(String endpoint) {
      this.endpoint = endpoint;
    }

    @Override
    public void handle(HttpExchange exchange) throws IOException {
      // Increment counter with both labels
      appRequestCounter.labels(endpoint, APP_NAME).inc();

      String response = "Hello from " + endpoint;
      exchange.sendResponseHeaders(200, response.length());

      try (OutputStreamWriter os = new OutputStreamWriter(exchange.getResponseBody())) {
        os.write(response);
      }
    }
  }
}

Run the application.

 

You will see following messages in console.

 

Server started on http://localhost:8080

Metrics available at: http://localhost:8080/metrics

 

Open the url ‘http://localhost:8080/metrics’ in browser.

 


Right now no metrics are captured. Let’s hit the http://localhost:8080/health endpoint couple of times. I hit this endpoint for 5 times, and the metrics response is like below.

# HELP app_total_count_total Total requests by endpoint and app name
# TYPE app_total_count_total counter
app_total_count_total{endpoint="/health",app_name="my_app",} 5.0
# HELP app_total_count_created Total requests by endpoint and app name
# TYPE app_total_count_created gauge
app_total_count_created{endpoint="/health",app_name="my_app",} 1.744526450145E9

From the response, you can confirm that the /health endpoint is visited 5 times.

 

Let’s hit /login 3 times, /logout 2 times and /user-profile 1 time.

 

Metrics endpoint response looks like below.

 

# HELP app_total_count_total Total requests by endpoint and app name
# TYPE app_total_count_total counter
app_total_count_total{endpoint="/logout",app_name="my_app",} 2.0
app_total_count_total{endpoint="/login",app_name="my_app",} 3.0
app_total_count_total{endpoint="/health",app_name="my_app",} 5.0
app_total_count_total{endpoint="/user-profile",app_name="my_app",} 1.0
# HELP app_total_count_created Total requests by endpoint and app name
# TYPE app_total_count_created gauge
app_total_count_created{endpoint="/logout",app_name="my_app",} 1.744526536605E9
app_total_count_created{endpoint="/login",app_name="my_app",} 1.744526531462E9
app_total_count_created{endpoint="/health",app_name="my_app",} 1.744526450145E9
app_total_count_created{endpoint="/user-profile",app_name="my_app",} 1.744526539912E9

How to see this metrics in prometheus UI?

Let’s attach this target to Prometheus instance by updating prometheus.yml file

 

prometheus.yml

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']
  - job_name: 'node_exporter'
    static_configs:
      - targets: ['localhost:9100']
  - job_name: 'java_app'
    static_configs:
      - targets: ['localhost:8080']

 

Start Prometheus application by executing following command.

prometheus --config.file=./prometheus.yml

 

Upon successful starting of Prometheus server, execute following metric.

app_total_count_total 


 

You can get total requests by the metric using the aggregate function sum.

 

sum(app_total_count_total)

 


Percentage of Total metrics went to user-profile

 

(
  sum(app_total_count_total{endpoint="/user-profile"})
  /
  sum(app_total_count_total)
) * 100

 


 

Filter the metrics by endpoint label

app_total_count_total{endpoint="/logout"}



 

Previous                                                    Next                                                    Home

No comments:

Post a Comment