Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ public Builder dataPoint(CounterDataPointSnapshot dataPoint) {
return this;
}

@Override
protected MetricMetadata buildMetadata() {
if (name == null) {
throw new IllegalArgumentException("Missing required field: name is null");
}
return MetricMetadataSupport.counterMetadata(name, help, unit);
}

@Override
public CounterSnapshot build() {
return new CounterSnapshot(buildMetadata(), dataPoints);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ public Builder unit(@Nullable Unit unit) {
throw new IllegalArgumentException("Info metric cannot have a unit.");
}

@Override
protected MetricMetadata buildMetadata() {
if (name == null) {
throw new IllegalArgumentException("Missing required field: name is null");
}
return MetricMetadataSupport.infoMetadata(name, help);
}

@Override
public InfoSnapshot build() {
return new InfoSnapshot(buildMetadata(), dataPoints);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package io.prometheus.metrics.model.snapshots;

import io.prometheus.metrics.model.registry.MetricType;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.annotation.Nullable;

/** Registration-time descriptor for a metric family. */
public final class MetricFamilyDescriptor {

private final MetricType type;
private final MetricMetadata metadata;
private final Set<String> labelNames;

private MetricFamilyDescriptor(
MetricType type, MetricMetadata metadata, Collection<String> labelNames) {
this.type = type;
this.metadata = metadata;
this.labelNames = Collections.unmodifiableSet(new LinkedHashSet<>(labelNames));
}

public static Builder<?> of(MetricType type, String name) {
switch (type) {
case COUNTER:
return counter(name);
case GAUGE:
return gauge(name);
case HISTOGRAM:
return histogram(name);
case SUMMARY:
return summary(name);
case INFO:
return info(name);
case STATESET:
return stateSet(name);
case UNKNOWN:
default:
return unknown(name);
}
}

public static CounterBuilder counter(String name) {
return new CounterBuilder().name(name);
}

public static GaugeBuilder gauge(String name) {
return new GaugeBuilder().name(name);
}

public static HistogramBuilder histogram(String name) {
return new HistogramBuilder().name(name);
}

public static SummaryBuilder summary(String name) {
return new SummaryBuilder().name(name);
}

public static InfoBuilder info(String name) {
return new InfoBuilder().name(name);
}

public static StateSetBuilder stateSet(String name) {
return new StateSetBuilder().name(name);
}

public static UnknownBuilder unknown(String name) {
return new UnknownBuilder().name(name);
}

public MetricType getType() {
return type;
}

public MetricMetadata getMetadata() {
return metadata;
}

public Set<String> getLabelNames() {
return labelNames;
}

public String getPrometheusName() {
return metadata.getPrometheusName();
}

public abstract static class Builder<T extends Builder<T>> {

@Nullable protected String name;
@Nullable protected String help;
@Nullable protected Unit unit;
protected final Set<String> labelNames = new LinkedHashSet<>();

public T name(String name) {
this.name = name;
return self();
}

public T help(@Nullable String help) {
this.help = help;
return self();
}

public T unit(@Nullable Unit unit) {
this.unit = unit;
return self();
}

public T labelName(String labelName) {
this.labelNames.add(labelName);
return self();
}

public T labelNames(String... labelNames) {
Collections.addAll(this.labelNames, labelNames);
return self();
}

public T labelNames(Collection<String> labelNames) {
this.labelNames.addAll(labelNames);
return self();
}

public MetricFamilyDescriptor build() {
return new MetricFamilyDescriptor(getType(), buildMetadata(), labelNames);
}

protected MetricMetadata buildMetadata() {
if (name == null) {
throw new IllegalArgumentException("Missing required field: name is null");
}
return MetricMetadataSupport.metricMetadata(name, help, unit);
}

protected abstract MetricType getType();

protected abstract T self();
}

public static final class CounterBuilder extends Builder<CounterBuilder> {

@Override
protected MetricMetadata buildMetadata() {
if (name == null) {
throw new IllegalArgumentException("Missing required field: name is null");
}
return MetricMetadataSupport.counterMetadata(name, help, unit);
}

@Override
protected MetricType getType() {
return MetricType.COUNTER;
}

@Override
protected CounterBuilder self() {
return this;
}
}

public static final class GaugeBuilder extends Builder<GaugeBuilder> {

@Override
protected MetricType getType() {
return MetricType.GAUGE;
}

@Override
protected GaugeBuilder self() {
return this;
}
}

public static final class HistogramBuilder extends Builder<HistogramBuilder> {

@Override
protected MetricType getType() {
return MetricType.HISTOGRAM;
}

@Override
protected HistogramBuilder self() {
return this;
}
}

public static final class SummaryBuilder extends Builder<SummaryBuilder> {

@Override
protected MetricType getType() {
return MetricType.SUMMARY;
}

@Override
protected SummaryBuilder self() {
return this;
}
}

public static final class InfoBuilder extends Builder<InfoBuilder> {

@Override
public InfoBuilder unit(@Nullable Unit unit) {
throw new IllegalArgumentException("Info metric cannot have a unit.");
}

@Override
protected MetricMetadata buildMetadata() {
if (name == null) {
throw new IllegalArgumentException("Missing required field: name is null");
}
return MetricMetadataSupport.infoMetadata(name, help);
}

@Override
protected MetricType getType() {
return MetricType.INFO;
}

@Override
protected InfoBuilder self() {
return this;
}
}

public static final class StateSetBuilder extends Builder<StateSetBuilder> {

@Override
public StateSetBuilder unit(@Nullable Unit unit) {
throw new IllegalArgumentException("State set metric cannot have a unit.");
}

@Override
protected MetricType getType() {
return MetricType.STATESET;
}

@Override
protected StateSetBuilder self() {
return this;
}
}

public static final class UnknownBuilder extends Builder<UnknownBuilder> {

@Override
protected MetricType getType() {
return MetricType.UNKNOWN;
}

@Override
protected UnknownBuilder self() {
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.prometheus.metrics.model.snapshots;

import javax.annotation.Nullable;

final class MetricMetadataSupport {

private MetricMetadataSupport() {}

static MetricMetadata metricMetadata(String name, @Nullable String help, @Nullable Unit unit) {
return new MetricMetadata(name, help, unit);
}

static MetricMetadata counterMetadata(String name, @Nullable String help, @Nullable Unit unit) {
return typedMetadata(name, help, unit, "_total", ".total");
}

static MetricMetadata infoMetadata(String name, @Nullable String help) {
return typedMetadata(name, help, null, "_info", ".info");
}

private static MetricMetadata typedMetadata(
String originalName,
@Nullable String help,
@Nullable Unit unit,
String suffix,
String dotSuffix) {
String baseName = stripSuffix(originalName, suffix, dotSuffix);
return new MetricMetadata(
appendUnitIfMissing(baseName, unit),
appendUnitIfMissing(originalName, unit),
originalName,
help,
unit);
}

private static String appendUnitIfMissing(String name, @Nullable Unit unit) {
if (unit != null && !name.endsWith("_" + unit) && !name.endsWith("." + unit)) {
return name + "_" + unit;
}
return name;
}

private static String stripSuffix(String name, String suffix, String dotSuffix) {
if (name.endsWith(suffix)) {
return name.substring(0, name.length() - suffix.length());
}
if (name.endsWith(dotSuffix)) {
return name.substring(0, name.length() - dotSuffix.length());
}
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ private static <T extends DataPointSnapshot> void validateLabels(

public abstract static class Builder<T extends Builder<T>> {

@Nullable private String name;
@Nullable private String help;
@Nullable private Unit unit;
@Nullable protected String name;
@Nullable protected String help;
@Nullable protected Unit unit;

/**
* The name is required. If the name is missing or invalid, {@code build()} will throw an {@link
Expand Down Expand Up @@ -85,7 +85,7 @@ protected MetricMetadata buildMetadata() {
if (name == null) {
throw new IllegalArgumentException("Missing required field: name is null");
}
return new MetricMetadata(name, help, unit);
return MetricMetadataSupport.metricMetadata(name, help, unit);
}

protected abstract T self();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ void testMinimalGoodCase() {
.dataPoint(CounterDataPointSnapshot.builder().value(1.0).build())
.build();
SnapshotTestUtil.assertMetadata(snapshot, "events", null, null);
SnapshotTestUtil.assertDerivedMetadata(snapshot, "events", "events", "events");
assertThat(snapshot.getDataPoints()).hasSize(1);
CounterDataPointSnapshot data = snapshot.getDataPoints().get(0);
assertThat((Iterable<? extends Label>) data.getLabels()).isEmpty();
Expand All @@ -93,7 +94,18 @@ void testEmptyCounter() {
@Test
void testTotalSuffixPresent() {
CounterSnapshot snapshot = CounterSnapshot.builder().name("test_total").build();
assertThat(snapshot.getMetadata().getPrometheusName()).isEqualTo("test_total");
assertThat(snapshot.getMetadata().getPrometheusName()).isEqualTo("test");
SnapshotTestUtil.assertDerivedMetadata(snapshot, "test", "test_total", "test_total");
}

@Test
void testCounterUnitDerivedFromTypedBuilder() {
CounterSnapshot snapshot =
CounterSnapshot.builder().name("test_total").unit(Unit.SECONDS).build();

SnapshotTestUtil.assertMetadata(snapshot, "test_seconds", null, "seconds");
SnapshotTestUtil.assertDerivedMetadata(
snapshot, "test_seconds", "test_total_seconds", "test_total");
}

@Test
Expand Down
Loading