001package io.prometheus.client; 002 003import io.prometheus.client.CKMSQuantiles.Quantile; 004 005import java.io.Closeable; 006import java.util.ArrayList; 007import java.util.Arrays; 008import java.util.Collections; 009import java.util.List; 010import java.util.Map; 011import java.util.Set; 012import java.util.LinkedHashSet; 013import java.util.concurrent.Callable; 014import java.util.concurrent.TimeUnit; 015 016/** 017 * Enumeration metric, to track which of a set of states something is in. 018 * 019 * The first provided state will be the default. 020 * 021 * <p> 022 * Example enumeration: 023 * <pre> 024 * {@code 025 * class YourClass { 026 * static final Enumeration taskState = Enumeration.build() 027 * .name("task_state").help("State of the task.") 028 * .states("stopped", "starting", "running") 029 * .register(); 030 * 031 * void stop() { 032 * // Your code here. 033 * taskState.state("stopped") 034 * } 035 * } 036 * } 037 * </pre> 038 * 039 * You can also use a Java Enum: 040 * <pre> 041 * class YourClass { 042 * public enum yourEnum { 043 * STOPPED, 044 * STARTING, 045 * RUNNING, 046 * } 047 * static final Enumeration taskState = Enumeration.build() 048 * .name("task_state").help("State of the task.") 049 * .states(yourEnum.class) 050 * .register(); 051 * 052 * void stop() { 053 * // Your code here. 054 * taskState.state(yourEnum.STOPPED) 055 * } 056 * } 057 * } 058 * </pre> 059 */ 060public class Enumeration extends SimpleCollector<Enumeration.Child> implements Counter.Describable { 061 062 private final Set<String> states; 063 064 Enumeration(Builder b) { 065 super(b); 066 for (String label : labelNames) { 067 if (label.equals(fullname)) { 068 throw new IllegalStateException("Enumeration cannot have a label named the same as its metric name."); 069 } 070 } 071 states = b.states; 072 initializeNoLabelsChild(); 073 } 074 075 public static class Builder extends SimpleCollector.Builder<Builder, Enumeration> { 076 077 private Set<String> states; 078 079 public Builder states(String... s) { 080 if (s.length == 0) { 081 throw new IllegalArgumentException("There must be at least one state"); 082 } 083 // LinkedHashSet so we can know which was the first state. 084 states = new LinkedHashSet(); 085 states.addAll(Arrays.asList(s)); 086 return this; 087 } 088 089 /** 090 * Take states from the names of the values in an Enum class. 091 */ 092 public Builder states(Class e) { 093 Object[] vals = e.getEnumConstants(); 094 String[] s = new String[vals.length]; 095 for(int i = 0; i < vals.length; i++) { 096 s[i] = ((Enum)vals[i]).name(); 097 } 098 return states(s); 099 } 100 101 @Override 102 public Enumeration create() { 103 if (states == null) { 104 throw new IllegalStateException("Enumeration states must be specified."); 105 } 106 if (!unit.isEmpty()) { 107 throw new IllegalStateException("Enumeration metrics cannot have a unit."); 108 } 109 dontInitializeNoLabelsChild = true; 110 return new Enumeration(this); 111 } 112 } 113 114 /** 115 * Return a Builder to allow configuration of a new Enumeration. Ensures required fields are provided. 116 * 117 * @param name The name of the metric 118 * @param help The help string of the metric 119 */ 120 public static Builder build(String name, String help) { 121 return new Builder().name(name).help(help); 122 } 123 124 /** 125 * Return a Builder to allow configuration of a new Enumeration. 126 */ 127 public static Builder build() { 128 return new Builder(); 129 } 130 131 @Override 132 protected Child newChild() { 133 return new Child(states); 134 } 135 136 137 /** 138 * The value of a single Enumeration. 139 * <p> 140 * <em>Warning:</em> References to a Child become invalid after using 141 * {@link SimpleCollector#remove} or {@link SimpleCollector#clear}. 142 */ 143 public static class Child { 144 145 private String value; 146 private final Set<String> states; 147 148 private Child(Set<String> states) { 149 this.states = states; 150 value = states.iterator().next(); // Initialize with the first state. 151 } 152 153 /** 154 * Set the state. 155 */ 156 public void state(String s) { 157 if (!states.contains(s)) { 158 throw new IllegalArgumentException("Unknown state " + s); 159 } 160 value = s; 161 } 162 163 /** 164 * Set the state. 165 */ 166 public void state(Enum e) { 167 state(e.name()); 168 } 169 170 /** 171 * Get the state. 172 */ 173 public String get() { 174 return value; 175 } 176 } 177 178 // Convenience methods. 179 /** 180 * Set the state on the enum with no labels. 181 */ 182 public void state(String s) { 183 noLabelsChild.state(s); 184 } 185 186 /** 187 * Set the state on the enum with no labels. 188 */ 189 public void state(Enum e) { 190 noLabelsChild.state(e); 191 } 192 193 /** 194 * Get the value of the Enumeration. 195 */ 196 public String get() { 197 return noLabelsChild.get(); 198 } 199 200 @Override 201 public List<MetricFamilySamples> collect() { 202 List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>(); 203 for(Map.Entry<List<String>, Child> c: children.entrySet()) { 204 String v = c.getValue().get(); 205 List<String> labelNamesWithState = new ArrayList<String>(labelNames); 206 labelNamesWithState.add(fullname); 207 for(String s : states) { 208 List<String> labelValuesWithState = new ArrayList<String>(c.getKey()); 209 labelValuesWithState.add(s); 210 samples.add(new MetricFamilySamples.Sample(fullname, labelNamesWithState, labelValuesWithState, s.equals(v) ? 1.0 : 0.0)); 211 } 212 } 213 214 return familySamplesList(Type.STATE_SET, samples); 215 } 216 217 @Override 218 public List<MetricFamilySamples> describe() { 219 return Collections.singletonList( 220 new MetricFamilySamples(fullname, Type.STATE_SET, help, Collections.<MetricFamilySamples.Sample>emptyList())); 221 } 222 223}