package io.prometheus.client; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.List; /** * A registry of Collectors. * <p> * The majority of users should use the {@link #defaultRegistry}, rather than instantiating their own. * <p> * Creating a registry other than the default is primarily useful for unittests, or * pushing a subset of metrics to the <a href="https://github.com/prometheus/pushgateway">Pushgateway</a> * from batch jobs. */ public class CollectorRegistry { /** * The default registry. */ public static final CollectorRegistry defaultRegistry = new CollectorRegistry(true); private final Map<Collector, List<String>> collectorsToNames = new HashMap<Collector, List<String>>(); private final Map<String, Collector> namesToCollectors = new HashMap<String, Collector>(); private final boolean autoDescribe; public CollectorRegistry(){ this(false); } public CollectorRegistry(boolean autoDescribe) { this.autoDescribe = autoDescribe; } /** * Register a Collector. * <p> * A collector can be registered to multiple CollectorRegistries. */ public void register(Collector m) { List<String> names = collectorNames(m); synchronized (collectorsToNames) { for (String name : names) { if(namesToCollectors.containsKey(name)) { throw new IllegalArgumentException("Collector already registered that provides name: " + name); } } for (String name : names) { namesToCollectors.put(name, m); } collectorsToNames.put(m, names); } } /** * Unregister a Collector. */ public void unregister(Collector m) { synchronized (collectorsToNames) { for (String name : collectorsToNames.get(m)) { namesToCollectors.remove(name); } collectorsToNames.remove(m); } } /** * Unregister all Collectors. */ public void clear() { synchronized (collectorsToNames) { collectorsToNames.clear(); namesToCollectors.clear(); } } /** * A snapshot of the current collectors. */ private Set<Collector> collectors() { synchronized (collectorsToNames) { return new HashSet<Collector>(collectorsToNames.keySet()); } } private List<String> collectorNames(Collector m) { List<Collector.MetricFamilySamples> mfs; if (m instanceof Collector.Describable) { mfs = ((Collector.Describable)m).describe(); } else if (autoDescribe) { mfs = m.collect(); } else { mfs = Collections.emptyList(); } List<String> names = new ArrayList<String>(); for (Collector.MetricFamilySamples family : mfs) { switch (family.type) { case SUMMARY: names.add(family.name + "_count"); names.add(family.name + "_sum"); names.add(family.name); case HISTOGRAM: names.add(family.name + "_count"); names.add(family.name + "_sum"); names.add(family.name + "_bucket"); default: names.add(family.name); } } return names; } /** * Enumeration of metrics of all registered collectors. */ public Enumeration<Collector.MetricFamilySamples> metricFamilySamples() { return new MetricFamilySamplesEnumeration(); } class MetricFamilySamplesEnumeration implements Enumeration<Collector.MetricFamilySamples> { private final Iterator<Collector> collectorIter = collectors().iterator(); private Iterator<Collector.MetricFamilySamples> metricFamilySamples; private Collector.MetricFamilySamples next; MetricFamilySamplesEnumeration() { findNextElement(); } private void findNextElement() { if (metricFamilySamples != null && metricFamilySamples.hasNext()) { next = metricFamilySamples.next(); } else { while (collectorIter.hasNext()) { metricFamilySamples = collectorIter.next().collect().iterator(); if (metricFamilySamples.hasNext()) { next = metricFamilySamples.next(); return; } } next = null; } } public Collector.MetricFamilySamples nextElement() { Collector.MetricFamilySamples current = next; if (current == null) { throw new NoSuchElementException(); } findNextElement(); return current; } public boolean hasMoreElements() { return next != null; } } /** * Returns the given value, or null if it doesn't exist. * <p> * This is inefficient, and intended only for use in unittests. */ public Double getSampleValue(String name) { return getSampleValue(name, new String[]{}, new String[]{}); } /** * Returns the given value, or null if it doesn't exist. * <p> * This is inefficient, and intended only for use in unittests. */ public Double getSampleValue(String name, String[] labelNames, String[] labelValues) { for (Collector.MetricFamilySamples metricFamilySamples: Collections.list(metricFamilySamples())) { for (Collector.MetricFamilySamples.Sample sample: metricFamilySamples.samples) { if (sample.name.equals(name) && Arrays.equals(sample.labelNames.toArray(), labelNames) && Arrays.equals(sample.labelValues.toArray(), labelValues)) { return sample.value; } } } return null; } }