Skip to content

Commit

Permalink
Add support for Info metrics
Browse files Browse the repository at this point in the history
Signed-off-by: Brian Brazil <[email protected]>
  • Loading branch information
brian-brazil committed Jan 18, 2021
1 parent 92d34e3 commit 37fa7fb
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ private List<String> collectorNames(Collector m) {
names.add(family.name + "_bucket");
names.add(family.name);
break;
case INFO:
names.add(family.name + "_info");
names.add(family.name);
break;
default:
names.add(family.name);
}
Expand Down
177 changes: 177 additions & 0 deletions simpleclient/src/main/java/io/prometheus/client/Info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package io.prometheus.client;

import io.prometheus.client.CKMSQuantiles.Quantile;

import java.io.Closeable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
* Info metric, key-value pairs.
*
* Examples of Info include, build information, version information, and potential target metadata,
* The first provided state will be the default.
*
* <p>
* Example enumeration:
* <pre>
* {@code
* class YourClass {
* static final Info buildInfo = Info.build()
* .name("your_build_info").help("Build information.")
* .register();
*
* void func() {
* // Your code here.
* buildInfo.info("branch", "HEAD", "version", "1.2.3", "revision", "e0704b");
* }
* }
* }
* </pre>
*/
public class Info extends SimpleCollector<Info.Child> implements Counter.Describable {

Info(Builder b) {
super(b);
}

public static class Builder extends SimpleCollector.Builder<Builder, Info> {
@Override
public Info create() {
if (!unit.isEmpty()) {
throw new IllegalStateException("Info metrics cannot have a unit.");
}
return new Info(this);
}
}

/**
* Return a Builder to allow configuration of a new Info. Ensures required fields are provided.
*
* @param name The name of the metric
* @param help The help string of the metric
*/
public static Builder build(String name, String help) {
return new Builder().name(name).help(help);
}

/**
* Return a Builder to allow configuration of a new Info.
*/
public static Builder build() {
return new Builder();
}

@Override
protected Child newChild() {
return new Child(labelNames);
}


/**
* The value of a single Info.
* <p>
* <em>Warning:</em> References to a Child become invalid after using
* {@link SimpleCollector#remove} or {@link SimpleCollector#clear}.
*/
public static class Child {

private Map<String, String> value = Collections.emptyMap();
private List<String> labelNames;

private Child(List<String> labelNames) {
this.labelNames = labelNames;
}

/**
* Set the info.
*/
public void info(Map<String, String> v) {
for (String key : v.keySet()) {
checkMetricLabelName(key);
}
for (String label : labelNames) {
if (v.containsKey(label)) {
throw new IllegalArgumentException("Info and its value cannot have the same label name.");
}
}
this.value = v;
}
/**
* Set the info.
*
* @param v labels as pairs of key values
*/
public void info(String... v) {
if (v.length % 2 != 0) {
throw new IllegalArgumentException("An even number of arguments must be passed");
}
Map<String, String> m = new TreeMap<String, String>();
for (int i = 0; i < v.length; i+=2) {
m.put(v[i], v[i+1]);
}
info(m);
}

/**
* Get the info.
*/
public Map<String, String> get() {
return value;
}
}

// Convenience methods.
/**
* Set the info on the info with no labels.
*/
public void info(String... v) {
noLabelsChild.info(v);
}

/**
* Set the info on the info with no labels.
*
* @param v labels as pairs of key values
*/
public void info(Map<String, String> v) {
noLabelsChild.info(v);
}

/**
* Get the the info.
*/
public Map<String, String> get() {
return noLabelsChild.get();
}

@Override
public List<MetricFamilySamples> collect() {
List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>();
for(Map.Entry<List<String>, Child> c: children.entrySet()) {
Map<String, String> v = c.getValue().get();
List<String> names = new ArrayList<String>(labelNames);
List<String> values = new ArrayList<String>(c.getKey());
for(Map.Entry<String, String> l: v.entrySet()) {
names.add(l.getKey());
values.add(l.getValue());
}
samples.add(new MetricFamilySamples.Sample(fullname + "_info", names, values, 1.0));
}

return familySamplesList(Type.INFO, samples);
}

@Override
public List<MetricFamilySamples> describe() {
return Collections.singletonList(
new MetricFamilySamples(fullname, Type.INFO, help, Collections.<MetricFamilySamples.Sample>emptyList()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void testLabels() {
assertEquals(1.0, getLabeledState("b", "bar"), .001);
}

public enum myEnum {
enum myEnum {
FOO,
BAR,
}
Expand Down
96 changes: 96 additions & 0 deletions simpleclient/src/test/java/io/prometheus/client/InfoTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package io.prometheus.client;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;


public class InfoTest {

CollectorRegistry registry;
Info noLabels, labels;

@Before
public void setUp() {
registry = new CollectorRegistry();
noLabels = Info.build().name("nolabels").help("help").register(registry);
labels = Info.build().name("labels").help("help").labelNames("l").register(registry);
}

private Double getInfo(String metric, String... labels) {
String[] names = new String[labels.length / 2];
String[] values = new String[labels.length / 2];
for (int i = 0; i < labels.length; i+=2) {
names[i/2] = labels[i];
values[i/2] = labels[i+1];
}
return registry.getSampleValue(metric + "_info", names, values);
}

@Test
public void testInfo() {
assertEquals(null, getInfo("nolabels", "foo", "bar"));
noLabels.info("foo", "bar");
assertEquals(1.0, getInfo("nolabels", "foo", "bar"), .001);
noLabels.info("foo", "bar", "baz", "meh");
assertEquals(null, getInfo("nolabels", "foo", "bar"));
assertEquals(1.0, getInfo("nolabels", "baz", "meh", "foo", "bar"), .001);
}

@Test
public void testDefaultValue() {
assertEquals(1.0, getInfo("nolabels"), .001);
}

@Test
public void testLabels() {
assertEquals(null, getInfo("labels", "l", "a", "foo", "bar"));
assertEquals(null, getInfo("labels", "l", "b", "baz", "meh"));
labels.labels("a").info("foo", "bar");
assertEquals(1.0, getInfo("labels", "l", "a", "foo", "bar"), .001);
assertEquals(null, getInfo("labels", "l", "b", "baz", "meh"));
labels.labels("b").info("baz", "meh");
assertEquals(1.0, getInfo("labels", "l", "a", "foo", "bar"), .001);
assertEquals(1.0, getInfo("labels", "l", "b", "baz", "meh"), .001);

assertEquals(null, getInfo("nolabels", "l", "a"));
assertEquals(null, getInfo("nolabels", "l", "b"));
}

@Test(expected=IllegalArgumentException.class)
public void testDuplicateNameLabelThrows() {
Info i = Info.build().name("labels").help("help").labelNames("l").create();
i.labels("a").info("l", "bar");
}

@Test(expected=IllegalArgumentException.class)
public void testOddInfoThrows() {
Info i = Info.build().name("labels").help("help").create();
i.info("odd");
}

@Test(expected=IllegalStateException.class)
public void testUnitThrows() {
Info.build().unit("seconds").name("nolabels").help("help").create();
}

@Test
public void testCollect() {
labels.labels("a").info("foo", "bar", "baz", "meh");
List<Collector.MetricFamilySamples> mfs = labels.collect();

ArrayList<Collector.MetricFamilySamples.Sample> samples = new ArrayList<Collector.MetricFamilySamples.Sample>();
samples.add(new Collector.MetricFamilySamples.Sample("labels_info", asList("l", "baz", "foo"), asList("a", "meh", "bar"), 1.0));
Collector.MetricFamilySamples mfsFixture = new Collector.MetricFamilySamples("labels", Collector.Type.INFO, "help", samples);

assertEquals(1, mfs.size());
assertEquals(mfsFixture, mfs.get(0));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ public static void write004(Writer writer, Enumeration<Collector.MetricFamilySam
if (metricFamilySamples.type == Collector.Type.COUNTER) {
writer.write("_total");
}
if (metricFamilySamples.type == Collector.Type.INFO) {
writer.write("_info");
}
writer.write(' ');
writeEscapedHelp(writer, metricFamilySamples.help);
writer.write('\n');
Expand All @@ -78,6 +81,9 @@ public static void write004(Writer writer, Enumeration<Collector.MetricFamilySam
if (metricFamilySamples.type == Collector.Type.COUNTER) {
writer.write("_total");
}
if (metricFamilySamples.type == Collector.Type.INFO) {
writer.write("_info");
}
writer.write(' ');
writer.write(typeString(metricFamilySamples.type));
writer.write('\n');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Info;
import io.prometheus.client.Summary;


Expand Down Expand Up @@ -61,6 +62,17 @@ public void testCounterOutput() throws IOException {
+ "# EOF\n", writer.toString().replaceAll("_created [0-9E.]+", "_created 1234.0"));
}

@Test
public void testInfoOutput() throws IOException {
Info noLabels = Info.build().name("nolabels").help("help").register(registry);
noLabels.info("foo", "bar");
TextFormat.writeOpenMetrics100(writer, registry.metricFamilySamples());
assertEquals("# TYPE nolabels info\n"
+ "# HELP nolabels help\n"
+ "nolabels_info{foo=\"bar\"} 1.0\n"
+ "# EOF\n", writer.toString());
}

@Test
public void testCounterSamplesMissingTotal() throws IOException {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Info;
import io.prometheus.client.Summary;


Expand Down Expand Up @@ -76,6 +77,15 @@ public void testCounterWithTotalOutput() throws IOException {
writer.toString().replaceAll("_created [0-9E.]+", "_created 1234.0"));
}

@Test
public void testInfoOutput() throws IOException {
Info noLabels = Info.build().name("nolabels").help("help").register(registry);
noLabels.info("foo", "bar");
TextFormat.write004(writer, registry.metricFamilySamples());
assertEquals("# HELP nolabels_info help\n"
+ "# TYPE nolabels_info gauge\n"
+ "nolabels_info{foo=\"bar\",} 1.0\n", writer.toString());
}

@Test
public void testCounterSamplesMissingTotal() throws IOException {
Expand Down

0 comments on commit 37fa7fb

Please sign in to comment.