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