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