MetricUtils.java

  1. package no.nav.data.common.utils;

  2. import com.github.benmanes.caffeine.cache.Cache;
  3. import io.prometheus.client.Counter;
  4. import io.prometheus.client.Gauge;
  5. import io.prometheus.client.SimpleCollector;
  6. import io.prometheus.client.Summary;
  7. import io.prometheus.client.cache.caffeine.CacheMetricsCollector;
  8. import lombok.extern.slf4j.Slf4j;
  9. import no.nav.data.common.exceptions.TechnicalException;
  10. import org.springframework.util.ReflectionUtils;

  11. import java.lang.reflect.Field;
  12. import java.util.ArrayList;
  13. import java.util.List;
  14. import java.util.Map;
  15. import java.util.concurrent.ConcurrentHashMap;

  16. import static java.util.Objects.requireNonNull;

  17. /**
  18.  * Helper to create metrics - avoid registring metrics multiple times during test - instantiate labels up front to initialize them in prometheus/grafana
  19.  */
  20. @Slf4j
  21. public final class MetricUtils {

  22.     public static final String DB_QUERY_TIMED = "db_query_timed";
  23.     public static final String QUERY = "query";
  24.     private static final Map<String, SimpleCollector<?>> collectors = new ConcurrentHashMap<>();
  25.     private static final CacheMetricsCollector cacheCollector = new CacheMetricsCollector().register();

  26.     private MetricUtils() {
  27.     }

  28.     public static CounterBuilder counter() {
  29.         return new CounterBuilder();
  30.     }

  31.     public static SummaryBuilder summary() {
  32.         return new SummaryBuilder();
  33.     }

  34.     public static GaugeBuilder gauge() {
  35.         return new GaugeBuilder();
  36.     }

  37.     public static <C extends Cache<K, V>, K, V> C register(String name, C cache) {
  38.         cacheCollector.addCache(name, cache);
  39.         return cache;
  40.     }

  41.     @SuppressWarnings("unchecked")
  42.     private static <T extends SimpleCollector<?>> T register(T collector, List<String[]> labels) {
  43.         try {
  44.             Field nameField = ReflectionUtils.findField(SimpleCollector.class, "fullname");
  45.             requireNonNull(nameField).setAccessible(true);
  46.             String name = ((String) nameField.get(collector));
  47.             SimpleCollector<?> registeredCollector = collectors.computeIfAbsent(name, mapName -> init(collector, labels));
  48.             if (registeredCollector.getClass().isAssignableFrom(collector.getClass())) {
  49.                 return (T) registeredCollector;
  50.             } else {
  51.                 throw new TechnicalException("Collector allready assigned to different type " + collector);
  52.             }
  53.         } catch (Exception e) {
  54.             throw new TechnicalException("failed to init collector", e);
  55.         }
  56.     }

  57.     private static <T extends SimpleCollector<?>> T init(T collector, List<String[]> labels) {
  58.         // Initialize labels
  59.         for (String[] label : labels) {
  60.             collector.labels(label);
  61.         }
  62.         return collector.register();
  63.     }

  64.     public static class CounterBuilder extends Counter.Builder {

  65.         private final List<String[]> labels = new ArrayList<>();

  66.         @Override
  67.         public Counter register() {
  68.             return MetricUtils.register(super.create(), labels);
  69.         }


  70.         public CounterBuilder labels(String... labels) {
  71.             this.labels.add(labels);
  72.             return this;
  73.         }
  74.     }

  75.     public static class SummaryBuilder extends Summary.Builder {

  76.         private List<String[]> labels = new ArrayList<>();

  77.         @Override
  78.         public Summary register() {
  79.             return MetricUtils.register(super.create(), labels);
  80.         }

  81.         public SummaryBuilder labels(String... labels) {
  82.             this.labels.add(labels);
  83.             return this;
  84.         }

  85.         public SummaryBuilder labels(List<String[]> labels) {
  86.             this.labels = labels;
  87.             return this;
  88.         }
  89.     }

  90.     public static class GaugeBuilder extends Gauge.Builder {

  91.         private List<String[]> labels = new ArrayList<>();

  92.         @Override
  93.         public Gauge register() {
  94.             return MetricUtils.register(super.create(), labels);
  95.         }

  96.         public GaugeBuilder labels(String... labels) {
  97.             this.labels.add(labels);
  98.             return this;
  99.         }

  100.         public GaugeBuilder labels(List<String[]> labels) {
  101.             this.labels = labels;
  102.             return this;
  103.         }
  104.     }
  105. }