001package io.prometheus.client; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.Collections; 006import java.util.Enumeration; 007import java.util.HashMap; 008import java.util.HashSet; 009import java.util.Iterator; 010import java.util.Map; 011import java.util.NoSuchElementException; 012import java.util.Set; 013import java.util.List; 014 015/** 016 * A registry of Collectors. 017 * <p> 018 * The majority of users should use the {@link #defaultRegistry}, rather than instantiating their own. 019 * <p> 020 * Creating a registry other than the default is primarily useful for unittests, or 021 * pushing a subset of metrics to the <a href="https://github.com/prometheus/pushgateway">Pushgateway</a> 022 * from batch jobs. 023 */ 024public class CollectorRegistry { 025 /** 026 * The default registry. 027 */ 028 public static final CollectorRegistry defaultRegistry = new CollectorRegistry(true); 029 030 031 private final Map<Collector, List<String>> collectorsToNames = new HashMap<Collector, List<String>>(); 032 private final Map<String, Collector> namesToCollectors = new HashMap<String, Collector>(); 033 034 private final boolean autoDescribe; 035 036 public CollectorRegistry(){ 037 this(false); 038 } 039 040 public CollectorRegistry(boolean autoDescribe) { 041 this.autoDescribe = autoDescribe; 042 } 043 044 /** 045 * Register a Collector. 046 * <p> 047 * A collector can be registered to multiple CollectorRegistries. 048 */ 049 public void register(Collector m) { 050 List<String> names = collectorNames(m); 051 synchronized (collectorsToNames) { 052 for (String name : names) { 053 if(namesToCollectors.containsKey(name)) { 054 throw new IllegalArgumentException("Collector already registered that provides name: " + name); 055 } 056 } 057 for (String name : names) { 058 namesToCollectors.put(name, m); 059 } 060 collectorsToNames.put(m, names); 061 } 062 } 063 064 /** 065 * Unregister a Collector. 066 */ 067 public void unregister(Collector m) { 068 synchronized (collectorsToNames) { 069 for (String name : collectorsToNames.get(m)) { 070 namesToCollectors.remove(name); 071 } 072 collectorsToNames.remove(m); 073 } 074 } 075 /** 076 * Unregister all Collectors. 077 */ 078 public void clear() { 079 synchronized (collectorsToNames) { 080 collectorsToNames.clear(); 081 namesToCollectors.clear(); 082 } 083 } 084 085 /** 086 * A snapshot of the current collectors. 087 */ 088 private Set<Collector> collectors() { 089 synchronized (collectorsToNames) { 090 return new HashSet(collectorsToNames.keySet()); 091 } 092 } 093 094 private List<String> collectorNames(Collector m) { 095 List<Collector.MetricFamilySamples> mfs; 096 if (m instanceof Collector.Describable) { 097 mfs = ((Collector.Describable)m).describe(); 098 } else if (autoDescribe) { 099 mfs = m.collect(); 100 } else { 101 mfs = Collections.emptyList(); 102 } 103 104 List<String> names = new ArrayList<String>(); 105 for (Collector.MetricFamilySamples family : mfs) { 106 switch (family.type) { 107 case SUMMARY: 108 names.add(family.name + "_count"); 109 names.add(family.name + "_sum"); 110 names.add(family.name); 111 case HISTOGRAM: 112 names.add(family.name + "_count"); 113 names.add(family.name + "_sum"); 114 names.add(family.name + "_bucket"); 115 default: 116 names.add(family.name); 117 } 118 } 119 return names; 120 } 121 122 /** 123 * Enumeration of metrics of all registered collectors. 124 */ 125 public Enumeration<Collector.MetricFamilySamples> metricFamilySamples() { 126 return new MetricFamilySamplesEnumeration(); 127 } 128 class MetricFamilySamplesEnumeration implements Enumeration<Collector.MetricFamilySamples> { 129 130 private final Iterator<Collector> collectorIter = collectors().iterator(); 131 private Iterator<Collector.MetricFamilySamples> metricFamilySamples; 132 private Collector.MetricFamilySamples next; 133 134 MetricFamilySamplesEnumeration() { 135 findNextElement(); 136 } 137 138 private void findNextElement() { 139 if (metricFamilySamples != null && metricFamilySamples.hasNext()) { 140 next = metricFamilySamples.next(); 141 } else { 142 while (collectorIter.hasNext()) { 143 metricFamilySamples = collectorIter.next().collect().iterator(); 144 if (metricFamilySamples.hasNext()) { 145 next = metricFamilySamples.next(); 146 return; 147 } 148 } 149 next = null; 150 } 151 } 152 153 public Collector.MetricFamilySamples nextElement() { 154 Collector.MetricFamilySamples current = next; 155 if (current == null) { 156 throw new NoSuchElementException(); 157 } 158 findNextElement(); 159 return current; 160 } 161 162 public boolean hasMoreElements() { 163 return next != null; 164 } 165 } 166 167 /** 168 * Returns the given value, or null if it doesn't exist. 169 * <p> 170 * This is inefficient, and intended only for use in unittests. 171 */ 172 public Double getSampleValue(String name) { 173 return getSampleValue(name, new String[]{}, new String[]{}); 174 } 175 176 /** 177 * Returns the given value, or null if it doesn't exist. 178 * <p> 179 * This is inefficient, and intended only for use in unittests. 180 */ 181 public Double getSampleValue(String name, String[] labelNames, String[] labelValues) { 182 for (Collector.MetricFamilySamples metricFamilySamples: Collections.list(metricFamilySamples())) { 183 for (Collector.MetricFamilySamples.Sample sample: metricFamilySamples.samples) { 184 if (sample.name.equals(name) 185 && Arrays.equals(sample.labelNames.toArray(), labelNames) 186 && Arrays.equals(sample.labelValues.toArray(), labelValues)) { 187 return sample.value; 188 } 189 } 190 } 191 return null; 192 } 193 194}