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; 009 010/** 011 * Example: 012 * 013 * <pre>{@code 014 * ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean(); 015 * 016 * CounterWithCallback.builder() 017 * .name("classes_loaded_total") 018 * .help("The total number of classes 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( 044 (value, labelValues) -> { 045 dataPoints.add( 046 new CounterSnapshot.CounterDataPointSnapshot( 047 value, makeLabels(labelValues), null, 0L)); 048 }); 049 return new CounterSnapshot(getMetadata(), dataPoints); 050 } 051 052 public static Builder builder() { 053 return new Builder(PrometheusProperties.get()); 054 } 055 056 public static Builder builder(PrometheusProperties properties) { 057 return new Builder(properties); 058 } 059 060 public static class Builder 061 extends CallbackMetric.Builder<CounterWithCallback.Builder, CounterWithCallback> { 062 063 private Consumer<Callback> callback; 064 065 public Builder callback(Consumer<Callback> callback) { 066 this.callback = callback; 067 return self(); 068 } 069 070 private Builder(PrometheusProperties properties) { 071 super(Collections.emptyList(), properties); 072 } 073 074 /** 075 * The {@code _total} suffix will automatically be appended if it's missing. 076 * 077 * <pre>{@code 078 * CounterWithCallback c1 = CounterWithCallback.builder() 079 * .name("events_total") 080 * .build(); 081 * CounterWithCallback c2 = CounterWithCallback.builder() 082 * .name("events") 083 * .build(); 084 * }</pre> 085 * 086 * In the example above both {@code c1} and {@code c2} would be named {@code "events_total"} in 087 * Prometheus. 088 * 089 * <p>Throws an {@link IllegalArgumentException} if {@link 090 * io.prometheus.metrics.model.snapshots.PrometheusNaming#isValidMetricName(String) 091 * MetricMetadata.isValidMetricName(name)} is {@code false}. 092 */ 093 @Override 094 public Builder name(String name) { 095 return super.name(Counter.stripTotalSuffix(name)); 096 } 097 098 @Override 099 public CounterWithCallback build() { 100 return new CounterWithCallback(this); 101 } 102 103 @Override 104 protected Builder self() { 105 return this; 106 } 107 } 108}