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