001package io.prometheus.metrics.core.metrics; 002 003import io.prometheus.metrics.config.PrometheusProperties; 004import io.prometheus.metrics.model.snapshots.CounterSnapshot; 005 006import java.util.ArrayList; 007import java.util.Collections; 008import java.util.List; 009import java.util.function.Consumer; 010 011/** 012 * Example: 013 * <pre>{@code 014 * ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean(); 015 * 016 * CounterWithCallback.builder() 017 * .name("classes_loaded_total") 018 * .help("The total number of classes that have been loaded since the JVM has started execution") 019 * .callback(callback -> callback.call(classLoadingMXBean.getLoadedClassCount())) 020 * .register(); 021 * }</pre> 022 */ 023public class CounterWithCallback extends CallbackMetric { 024 025 @FunctionalInterface 026 public interface Callback { 027 void call(double value, String... labelValues); 028 } 029 030 private final Consumer<Callback> callback; 031 032 private CounterWithCallback(Builder builder) { 033 super(builder); 034 this.callback = builder.callback; 035 if (callback == null) { 036 throw new IllegalArgumentException("callback cannot be null"); 037 } 038 } 039 040 @Override 041 public CounterSnapshot collect() { 042 List<CounterSnapshot.CounterDataPointSnapshot> dataPoints = new ArrayList<>(); 043 callback.accept((value, labelValues) -> { 044 dataPoints.add(new CounterSnapshot.CounterDataPointSnapshot(value, makeLabels(labelValues), null, 0L)); 045 }); 046 return new CounterSnapshot(getMetadata(), dataPoints); 047 } 048 049 public static Builder builder() { 050 return new Builder(PrometheusProperties.get()); 051 } 052 053 public static Builder builder(PrometheusProperties properties) { 054 return new Builder(properties); 055 } 056 057 public static class Builder extends CallbackMetric.Builder<CounterWithCallback.Builder, CounterWithCallback> { 058 059 private Consumer<Callback> callback; 060 061 public Builder callback(Consumer<Callback> callback) { 062 this.callback = callback; 063 return self(); 064 } 065 066 private Builder(PrometheusProperties properties) { 067 super(Collections.emptyList(), properties); 068 } 069 070 /** 071 * The {@code _total} suffix will automatically be appended if it's missing. 072 * <pre>{@code 073 * CounterWithCallback c1 = CounterWithCallback.builder() 074 * .name("events_total") 075 * .build(); 076 * CounterWithCallback c2 = CounterWithCallback.builder() 077 * .name("events") 078 * .build(); 079 * }</pre> 080 * In the example above both {@code c1} and {@code c2} would be named {@code "events_total"} in Prometheus. 081 * <p> 082 * Throws an {@link IllegalArgumentException} if 083 * {@link io.prometheus.metrics.model.snapshots.PrometheusNaming#isValidMetricName(String) MetricMetadata.isValidMetricName(name)} 084 * is {@code false}. 085 */ 086 @Override 087 public Builder name(String name) { 088 return super.name(Counter.stripTotalSuffix(name)); 089 } 090 091 @Override 092 public CounterWithCallback build() { 093 return new CounterWithCallback(this); 094 } 095 096 @Override 097 protected Builder self() { 098 return this; 099 } 100 } 101}