Skip to content

Commit 92d34e3

Browse files
committed
Add support for Enumeration.
Signed-off-by: Brian Brazil <[email protected]>
1 parent 5c0d1e7 commit 92d34e3

File tree

2 files changed

+337
-0
lines changed

2 files changed

+337
-0
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package io.prometheus.client;
2+
3+
import io.prometheus.client.CKMSQuantiles.Quantile;
4+
5+
import java.io.Closeable;
6+
import java.util.ArrayList;
7+
import java.util.Arrays;
8+
import java.util.Collections;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.Set;
12+
import java.util.LinkedHashSet;
13+
import java.util.concurrent.Callable;
14+
import java.util.concurrent.TimeUnit;
15+
16+
/**
17+
* Enumeration metric, to track which of a set of states something is in.
18+
*
19+
* The first provided state will be the default.
20+
*
21+
* <p>
22+
* Example enumeration:
23+
* <pre>
24+
* {@code
25+
* class YourClass {
26+
* static final Enumeration taskState = Enumeration.build()
27+
* .name("task_state").help("State of the task.")
28+
* .states("stopped", "starting", "running")
29+
* .register();
30+
*
31+
* void stop() {
32+
* // Your code here.
33+
* taskState.state("stopped")
34+
* }
35+
* }
36+
* }
37+
* </pre>
38+
*
39+
* You can also use a Java Enum:
40+
* <pre>
41+
* class YourClass {
42+
* public enum yourEnum {
43+
* STOPPED,
44+
* STARTING,
45+
* RUNNING,
46+
* }
47+
* static final Enumeration taskState = Enumeration.build()
48+
* .name("task_state").help("State of the task.")
49+
* .states(yourEnum.class)
50+
* .register();
51+
*
52+
* void stop() {
53+
* // Your code here.
54+
* taskState.state(yourEnum.STOPPED)
55+
* }
56+
* }
57+
* }
58+
* </pre>
59+
*/
60+
public class Enumeration extends SimpleCollector<Enumeration.Child> implements Counter.Describable {
61+
62+
private final Set<String> states;
63+
64+
Enumeration(Builder b) {
65+
super(b);
66+
for (String label : labelNames) {
67+
if (label.equals(fullname)) {
68+
throw new IllegalStateException("Enumeration cannot have a label named the same as its metric name.");
69+
}
70+
}
71+
states = b.states;
72+
initializeNoLabelsChild();
73+
}
74+
75+
public static class Builder extends SimpleCollector.Builder<Builder, Enumeration> {
76+
77+
private Set<String> states;
78+
79+
public Builder states(String... s) {
80+
if (s.length == 0) {
81+
throw new IllegalArgumentException("There must be at least one state");
82+
}
83+
// LinkedHashSet so we can know which was the first state.
84+
states = new LinkedHashSet();
85+
states.addAll(Arrays.asList(s));
86+
return this;
87+
}
88+
89+
/**
90+
* Take states from the names of the values in an Enum class.
91+
*/
92+
public Builder states(Class e) {
93+
Object[] vals = e.getEnumConstants();
94+
String[] s = new String[vals.length];
95+
for(int i = 0; i < vals.length; i++) {
96+
s[i] = ((Enum)vals[i]).name();
97+
}
98+
return states(s);
99+
}
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+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package io.prometheus.client;
2+
3+
import org.junit.After;
4+
import org.junit.Before;
5+
import org.junit.Test;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
import static java.util.Arrays.asList;
11+
import static org.junit.Assert.assertEquals;
12+
import static org.junit.Assert.assertTrue;
13+
14+
15+
public class EnumerationTest {
16+
17+
CollectorRegistry registry;
18+
Enumeration noLabels, labels;
19+
20+
@Before
21+
public void setUp() {
22+
registry = new CollectorRegistry();
23+
noLabels = Enumeration.build().states("foo", "bar").name("nolabels").help("help").register(registry);
24+
labels = Enumeration.build().states("foo", "bar").name("labels").help("help").labelNames("l").register(registry);
25+
}
26+
27+
private Double getNoLabelState(String s) {
28+
return registry.getSampleValue("nolabels", new String[]{"nolabels"}, new String[]{s});
29+
}
30+
private Double getLabeledState(String labelValue, String s) {
31+
return registry.getSampleValue("labels", new String[]{"l", "labels"}, new String[]{labelValue, s});
32+
}
33+
34+
@Test
35+
public void testState() {
36+
noLabels.state("bar");
37+
assertEquals(0.0, getNoLabelState("foo"), .001);
38+
assertEquals(1.0, getNoLabelState("bar"), .001);
39+
noLabels.state("foo");
40+
assertEquals(1.0, getNoLabelState("foo"), .001);
41+
assertEquals(0.0, getNoLabelState("bar"), .001);
42+
}
43+
44+
@Test
45+
public void testDefaultValue() {
46+
assertEquals(1.0, getNoLabelState("foo"), .001);
47+
assertEquals(0.0, getNoLabelState("bar"), .001);
48+
}
49+
50+
@Test
51+
public void testLabels() {
52+
assertEquals(null, getLabeledState("a", "foo"));
53+
assertEquals(null, getLabeledState("a", "bar"));
54+
assertEquals(null, getLabeledState("b", "foo"));
55+
assertEquals(null, getLabeledState("b", "bar"));
56+
labels.labels("a").state("foo");
57+
assertEquals(1.0, getLabeledState("a", "foo"), .001);
58+
assertEquals(0.0, getLabeledState("a", "bar"), .001);
59+
assertEquals(null, getLabeledState("b", "foo"));
60+
assertEquals(null, getLabeledState("b", "bar"));
61+
labels.labels("b").state("bar");
62+
assertEquals(1.0, getLabeledState("a", "foo"), .001);
63+
assertEquals(0.0, getLabeledState("a", "bar"), .001);
64+
assertEquals(0.0, getLabeledState("b", "foo"), .001);
65+
assertEquals(1.0, getLabeledState("b", "bar"), .001);
66+
}
67+
68+
public enum myEnum {
69+
FOO,
70+
BAR,
71+
}
72+
73+
@Test
74+
public void testJavaEnum() {
75+
Enumeration metric = Enumeration.build().states(myEnum.class).name("enum").help("help").register(registry);
76+
metric.state(myEnum.BAR);
77+
assertEquals(0.0, registry.getSampleValue("enum", new String[]{"enum"}, new String[]{"FOO"}), .001);
78+
assertEquals(1.0, registry.getSampleValue("enum", new String[]{"enum"}, new String[]{"BAR"}), .001);
79+
}
80+
81+
@Test(expected=IllegalStateException.class)
82+
public void testDuplicateNameLabelThrows() {
83+
Enumeration.build().states("foo", "bar").name("labels").help("help").labelNames("labels").create();
84+
}
85+
86+
@Test(expected=IllegalStateException.class)
87+
public void testNoStatesThrows() {
88+
Enumeration.build().name("nolabels").help("help").create();
89+
}
90+
91+
@Test(expected=IllegalArgumentException.class)
92+
public void testEmptyStatesThrows() {
93+
Enumeration.build().states().name("nolabels").help("help").create();
94+
}
95+
96+
@Test(expected=IllegalStateException.class)
97+
public void testUnitThrows() {
98+
Enumeration.build().states("foo", "bar").unit("seconds").name("nolabels").help("help").create();
99+
}
100+
101+
@Test
102+
public void testCollect() {
103+
labels.labels("a").state("bar");
104+
List<Collector.MetricFamilySamples> mfs = labels.collect();
105+
106+
ArrayList<Collector.MetricFamilySamples.Sample> samples = new ArrayList<Collector.MetricFamilySamples.Sample>();
107+
samples.add(new Collector.MetricFamilySamples.Sample("labels", asList("l", "labels"), asList("a", "foo"), 0.0));
108+
samples.add(new Collector.MetricFamilySamples.Sample("labels", asList("l", "labels"), asList("a", "bar"), 1.0));
109+
Collector.MetricFamilySamples mfsFixture = new Collector.MetricFamilySamples("labels", Collector.Type.STATE_SET, "help", samples);
110+
111+
assertEquals(1, mfs.size());
112+
assertEquals(mfsFixture, mfs.get(0));
113+
}
114+
}

0 commit comments

Comments
 (0)