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.List; 011import java.util.Map; 012import java.util.NoSuchElementException; 013import java.util.Set; 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 /** 077 * Unregister all Collectors. 078 */ 079 public void clear() { 080 synchronized (collectorsToNames) { 081 collectorsToNames.clear(); 082 namesToCollectors.clear(); 083 } 084 } 085 086 /** 087 * A snapshot of the current collectors. 088 */ 089 private Set<Collector> collectors() { 090 synchronized (collectorsToNames) { 091 return new HashSet<Collector>(collectorsToNames.keySet()); 092 } 093 } 094 095 private List<String> collectorNames(Collector m) { 096 List<Collector.MetricFamilySamples> mfs; 097 if (m instanceof Collector.Describable) { 098 mfs = ((Collector.Describable) m).describe(); 099 } else if (autoDescribe) { 100 mfs = m.collect(); 101 } else { 102 mfs = Collections.emptyList(); 103 } 104 105 List<String> names = new ArrayList<String>(); 106 for (Collector.MetricFamilySamples family : mfs) { 107 switch (family.type) { 108 case SUMMARY: 109 names.add(family.name + "_count"); 110 names.add(family.name + "_sum"); 111 names.add(family.name); 112 case HISTOGRAM: 113 names.add(family.name + "_count"); 114 names.add(family.name + "_sum"); 115 names.add(family.name + "_bucket"); 116 default: 117 names.add(family.name); 118 } 119 } 120 return names; 121 } 122 123 /** 124 * Enumeration of metrics of all registered collectors. 125 */ 126 public Enumeration<Collector.MetricFamilySamples> metricFamilySamples() { 127 return new MetricFamilySamplesEnumeration(); 128 } 129 130 public Enumeration<Collector.MetricFamilySamples> filteredMetricFamilySamples(Set<String> includedNames) { 131 return new MetricFamilySamplesEnumeration(includedNames); 132 } 133 134 class MetricFamilySamplesEnumeration implements Enumeration<Collector.MetricFamilySamples> { 135 136 private final Iterator<Collector> collectorIter; 137 private Iterator<Collector.MetricFamilySamples> metricFamilySamples; 138 private Collector.MetricFamilySamples next; 139 private Set<String> includedNames; 140 141 MetricFamilySamplesEnumeration(Set<String> includedNames) { 142 this.includedNames = includedNames; 143 collectorIter = includedCollectorIterator(includedNames); 144 findNextElement(); 145 } 146 147 private Iterator<Collector> includedCollectorIterator(Set<String> includedNames) { 148 if (includedNames.isEmpty()) { 149 return collectors().iterator(); 150 } else { 151 HashSet<Collector> collectors = new HashSet<Collector>(); 152 synchronized (namesToCollectors) { 153 for (Map.Entry<String, Collector> entry : namesToCollectors.entrySet()) { 154 if (includedNames.contains(entry.getKey())) { 155 collectors.add(entry.getValue()); 156 } 157 } 158 } 159 160 return collectors.iterator(); 161 } 162 } 163 164 MetricFamilySamplesEnumeration() { 165 this(Collections.<String>emptySet()); 166 } 167 168 private void findNextElement() { 169 next = null; 170 171 while (metricFamilySamples != null && metricFamilySamples.hasNext()) { 172 next = filter(metricFamilySamples.next()); 173 if (next != null) { 174 return; 175 } 176 } 177 178 if (next == null) { 179 while (collectorIter.hasNext()) { 180 metricFamilySamples = collectorIter.next().collect().iterator(); 181 while (metricFamilySamples.hasNext()) { 182 next = filter(metricFamilySamples.next()); 183 if (next != null) { 184 return; 185 } 186 } 187 } 188 } 189 } 190 191 private Collector.MetricFamilySamples filter(Collector.MetricFamilySamples next) { 192 if (includedNames.isEmpty()) { 193 return next; 194 } else { 195 Iterator<Collector.MetricFamilySamples.Sample> it = next.samples.iterator(); 196 while (it.hasNext()) { 197 if (!includedNames.contains(it.next().name)) { 198 it.remove(); 199 } 200 } 201 if (next.samples.size() == 0) { 202 return null; 203 } 204 return next; 205 } 206 } 207 208 public Collector.MetricFamilySamples nextElement() { 209 Collector.MetricFamilySamples current = next; 210 if (current == null) { 211 throw new NoSuchElementException(); 212 } 213 findNextElement(); 214 return current; 215 } 216 217 public boolean hasMoreElements() { 218 return next != null; 219 } 220 } 221 222 /** 223 * Returns the given value, or null if it doesn't exist. 224 * <p> 225 * This is inefficient, and intended only for use in unittests. 226 */ 227 public Double getSampleValue(String name) { 228 return getSampleValue(name, new String[]{}, new String[]{}); 229 } 230 231 /** 232 * Returns the given value, or null if it doesn't exist. 233 * <p> 234 * This is inefficient, and intended only for use in unittests. 235 */ 236 public Double getSampleValue(String name, String[] labelNames, String[] labelValues) { 237 for (Collector.MetricFamilySamples metricFamilySamples : Collections.list(metricFamilySamples())) { 238 for (Collector.MetricFamilySamples.Sample sample : metricFamilySamples.samples) { 239 if (sample.name.equals(name) 240 && Arrays.equals(sample.labelNames.toArray(), labelNames) 241 && Arrays.equals(sample.labelValues.toArray(), labelValues)) { 242 return sample.value; 243 } 244 } 245 } 246 return null; 247 } 248 249}