Skip to content
Open
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 @@ -92,8 +92,9 @@ public CounterSnapshot collect() {
@Override
protected CounterSnapshot collect(List<Labels> labels, List<DataPoint> metricData) {
List<CounterSnapshot.CounterDataPointSnapshot> data = new ArrayList<>(labels.size());
String metricName = metadata.getName();
for (int i = 0; i < labels.size(); i++) {
data.add(metricData.get(i).collect(labels.get(i)));
data.add(metricData.get(i).collect(labels.get(i), metricName));
}
return new CounterSnapshot(metadata, data);
}
Expand Down Expand Up @@ -199,7 +200,7 @@ private void validateAndAdd(double amount) {
doubleValue.add(amount);
}

private CounterSnapshot.CounterDataPointSnapshot collect(Labels labels) {
private CounterSnapshot.CounterDataPointSnapshot collect(Labels labels, String metricName) {
// Read the exemplar first. Otherwise, there is a race condition where you might
// see an Exemplar for a value that's not counted yet.
// If there are multiple Exemplars (by default it's just one), use the newest.
Expand All @@ -213,7 +214,7 @@ private CounterSnapshot.CounterDataPointSnapshot collect(Labels labels) {
}
}
return new CounterSnapshot.CounterDataPointSnapshot(
get(), labels, latestExemplar, createdTimeMillis);
get(), labels, latestExemplar, createdTimeMillis, metricName);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public CounterSnapshot collect() {
(value, labelValues) -> {
dataPoints.add(
new CounterSnapshot.CounterDataPointSnapshot(
value, makeLabels(labelValues), null, 0L));
value, makeLabels(labelValues), null, 0L, metadata.getName()));
});
return new CounterSnapshot(metadata, dataPoints);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public static class CounterDataPointSnapshot extends DataPointSnapshot {

private final double value;
@Nullable private final Exemplar exemplar;
/** Optional metric name used only in validation error messages. */
@Nullable private final String metricName;

/**
* To create a new {@link CounterDataPointSnapshot}, you can either call the constructor
Expand All @@ -62,7 +64,20 @@ public static class CounterDataPointSnapshot extends DataPointSnapshot {
*/
public CounterDataPointSnapshot(
double value, Labels labels, @Nullable Exemplar exemplar, long createdTimestampMillis) {
this(value, labels, exemplar, createdTimestampMillis, 0);
this(value, labels, exemplar, createdTimestampMillis, 0, null);
}

/**
* Same as {@link #CounterDataPointSnapshot(double, Labels, Exemplar, long)} with an optional
* metric name included in validation error messages when the value is negative.
*/
public CounterDataPointSnapshot(
double value,
Labels labels,
@Nullable Exemplar exemplar,
long createdTimestampMillis,
@Nullable String metricName) {
this(value, labels, exemplar, createdTimestampMillis, 0, metricName);
}

/**
Expand All @@ -77,7 +92,30 @@ public CounterDataPointSnapshot(
@Nullable Exemplar exemplar,
long createdTimestampMillis,
long scrapeTimestampMillis) {
this(value, labels, exemplar, createdTimestampMillis, scrapeTimestampMillis, false);
this(value, labels, exemplar, createdTimestampMillis, scrapeTimestampMillis, false, null);
}

/**
* Constructor with scrape timestamp and optional metric name for validation messages.
*
* @see #CounterDataPointSnapshot(double, Labels, Exemplar, long, long)
*/
@SuppressWarnings("this-escape")
public CounterDataPointSnapshot(
double value,
Labels labels,
@Nullable Exemplar exemplar,
long createdTimestampMillis,
long scrapeTimestampMillis,
@Nullable String metricName) {
this(
value,
labels,
exemplar,
createdTimestampMillis,
scrapeTimestampMillis,
false,
metricName);
}

@SuppressWarnings("this-escape")
Expand All @@ -88,9 +126,22 @@ public CounterDataPointSnapshot(
long createdTimestampMillis,
long scrapeTimestampMillis,
boolean internal) {
this(value, labels, exemplar, createdTimestampMillis, scrapeTimestampMillis, internal, null);
}

@SuppressWarnings("this-escape")
private CounterDataPointSnapshot(
double value,
Labels labels,
@Nullable Exemplar exemplar,
long createdTimestampMillis,
long scrapeTimestampMillis,
boolean internal,
@Nullable String metricName) {
super(labels, createdTimestampMillis, scrapeTimestampMillis, internal);
this.value = value;
this.exemplar = exemplar;
this.metricName = metricName;
if (!internal) {
validate();
}
Expand All @@ -107,7 +158,16 @@ public Exemplar getExemplar() {

protected void validate() {
if (value < 0.0) {
throw new IllegalArgumentException(value + ": counters cannot have a negative value");
StringBuilder message = new StringBuilder();
if (metricName != null && !metricName.isEmpty()) {
message.append(metricName).append('=');
}
message.append(value).append(": counters cannot have a negative value");
Labels labels = getLabels();
if (labels != null && !labels.isEmpty()) {
message.append(" (labels=").append(labels).append(')');
}
throw new IllegalArgumentException(message.toString());
}
}

Expand All @@ -119,7 +179,8 @@ DataPointSnapshot escape(EscapingScheme escapingScheme) {
SnapshotEscaper.escapeExemplar(exemplar, escapingScheme),
getCreatedTimestampMillis(),
getScrapeTimestampMillis(),
true);
true,
metricName);
}

public static Builder builder() {
Expand All @@ -131,6 +192,7 @@ public static class Builder extends DataPointSnapshot.Builder<Builder> {
@Nullable private Exemplar exemplar = null;
@Nullable private Double value = null;
private long createdTimestampMillis = 0L;
@Nullable private String metricName = null;

private Builder() {}

Expand All @@ -150,12 +212,26 @@ public Builder createdTimestampMillis(long createdTimestampMillis) {
return this;
}

/**
* Optional metric name included in the exception message when {@link #value(double)} is
* negative. Does not change the snapshot identity.
*/
public Builder metricName(@Nullable String metricName) {
this.metricName = metricName;
return this;
}

public CounterDataPointSnapshot build() {
if (value == null) {
throw new IllegalArgumentException("Missing required field: value is null.");
}
return new CounterDataPointSnapshot(
value, labels, exemplar, createdTimestampMillis, scrapeTimestampMillis);
value,
labels,
exemplar,
createdTimestampMillis,
scrapeTimestampMillis,
metricName);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,25 @@ void testDataImmutable() {
iterator.next();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(iterator::remove);
}

@Test
void testNegativeValueIncludesMetricNameInMessage() {
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(
() ->
CounterDataPointSnapshot.builder()
.metricName("http_requests")
.value(-2.0)
.build())
.withMessageContaining("http_requests")
.withMessageContaining("-2.0")
.withMessageContaining("counters cannot have a negative value");
}

@Test
void testNegativeValueWithoutMetricNameKeepsLegacyMessage() {
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> CounterDataPointSnapshot.builder().value(-1.0).build())
.withMessage("-1.0: counters cannot have a negative value");
}
}