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