11

I'm transforming a Spring Boot application from Spring Boot 1 (with the Prometheus Simpleclient) to Spring Boot 2 (which uses Micrometer).

I'm stumped at transforming the labels we have with Spring Boot 1 and Prometheus to concepts in Micrometer. For example (with Prometheus):

private static Counter requestCounter =
  Counter.build()
      .name("sent_requests_total")
      .labelNames("method", "path")
      .help("Total number of rest requests sent")
      .register();
...
requestCounter.labels(request.getMethod().name(), path).inc();

The tags of Micrometer seem to be something different than the labels of Prometheus: All values have to be predeclared, not only the keys.

Can one use Prometheus' labels with Spring (Boot) and Micrometer?

Martin Schröder
  • 4,176
  • 7
  • 47
  • 81

2 Answers2

23

Further digging showed that only the keys of micrometer tags have to be predeclared - but the constructor really takes pairs of key/values; the values don't matter. And the keys have to be specified when using the metric.

This works:

private static final String COUNTER_BATCHMANAGER_SENT_REQUESTS = "batchmanager.sent.requests";
private static final String METHOD_TAG = "method";
private static final String PATH_TAG = "path";
private final Counter requestCounter;
...
requestCounter = Counter.builder(COUNTER_BATCHMANAGER_SENT_REQUESTS)
    .description("Total number of rest requests sent")
    .tags(METHOD_TAG, "", PATH_TAG, "")
    .register(meterRegistry);
...
 Metrics.counter(COUNTER_BATCHMANAGER_SENT_REQUESTS, METHOD_TAG, methodName, PATH_TAG, path)
    .increment();
Martin Schröder
  • 4,176
  • 7
  • 47
  • 81
  • 10
    Micrometer's tags are the same as Prometheus' keys/labels. Compared to Prometheus' requirement to register the metric as a static variable, Micrometer allows avoiding that. In return you can declare the keys/labels at the same time. So you could avoid the first 'empty' metric declaration if you like and just keep the second `Metrics.counter` call. If you wanted to keep the description you could make the `register` call inline. Micrometer is forgiving in that case (compared to Prometheus) and actually does about the same lookup Prometheus does for the labels. – checketts Mar 10 '18 at 07:07
  • 1
    This works perfectly! You are a lifesaver. Basically what I understood is initialize all tags ( dimensions in Prometheus ) with null values and then populate them with specific values while incrementing it – Nilanjan Sarkar Feb 11 '20 at 14:36
  • I'm surprised there is now way clean way of using Prometheus labels with the `Counter.builder()`. – Martin Wickman Jun 08 '20 at 06:45
  • Prometheus documentation states: `CAUTION: Remember that every unique combination of key-value label pairs represents a new time series, which can dramatically increase the amount of data stored. Do not use labels to store dimensions with high cardinality (many different label values), such as user IDs, email addresses, or other unbounded sets of values.` This seems to imply that if you do not initialize every permutation of labels (keys and values) a new increment might still have its first increase missed? Or am I missing something? https://prometheus.io/docs/practices/naming/ – p.streef Apr 13 '22 at 07:53
2

I know that the topic is a lil bit of date but still. I maged to solve it like that with Spring Boot Acuator, by using PrometheusMeterRegistry to fetch CollectorRegistry

@Service
public class SomethingService {

    private final PrometheusMeterRegistry prometheusMeterRegistry;
    private final Counter counter;

    public SomethingService(PrometheusMeterRegistry prometheusMeterRegistry) {
        this.prometheusMeterRegistry = prometheusMeterRegistry;
        counter = Counter.build()
                .name("counter_name")
                .help("counter help")
                .labelNames("your_labels")
                .register(prometheusMeterRegistry.getPrometheusRegistry());
    }

    @PostConstruct
    private void init() {
        inc("your_value");
    }

    private void inc(String value) {
       counter.labels(String.valueOf(value)).inc();
    }
}