Add a new Gauge metric type, and hook it up to Prometheus and OTLP exporters. (#1788)

This commit is contained in:
John Watson 2020-10-16 10:09:52 -07:00 committed by GitHub
parent c01112b973
commit fe07cff3ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 265 additions and 30 deletions

View File

@ -9,16 +9,17 @@ import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATI
import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA; import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA;
import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_UNSPECIFIED; import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_UNSPECIFIED;
import io.opentelemetry.common.LabelConsumer;
import io.opentelemetry.common.Labels; import io.opentelemetry.common.Labels;
import io.opentelemetry.proto.common.v1.StringKeyValue; import io.opentelemetry.proto.common.v1.StringKeyValue;
import io.opentelemetry.proto.metrics.v1.AggregationTemporality; import io.opentelemetry.proto.metrics.v1.AggregationTemporality;
import io.opentelemetry.proto.metrics.v1.DoubleDataPoint; import io.opentelemetry.proto.metrics.v1.DoubleDataPoint;
import io.opentelemetry.proto.metrics.v1.DoubleGauge;
import io.opentelemetry.proto.metrics.v1.DoubleHistogram; import io.opentelemetry.proto.metrics.v1.DoubleHistogram;
import io.opentelemetry.proto.metrics.v1.DoubleHistogramDataPoint; import io.opentelemetry.proto.metrics.v1.DoubleHistogramDataPoint;
import io.opentelemetry.proto.metrics.v1.DoubleSum; import io.opentelemetry.proto.metrics.v1.DoubleSum;
import io.opentelemetry.proto.metrics.v1.InstrumentationLibraryMetrics; import io.opentelemetry.proto.metrics.v1.InstrumentationLibraryMetrics;
import io.opentelemetry.proto.metrics.v1.IntDataPoint; import io.opentelemetry.proto.metrics.v1.IntDataPoint;
import io.opentelemetry.proto.metrics.v1.IntGauge;
import io.opentelemetry.proto.metrics.v1.IntSum; import io.opentelemetry.proto.metrics.v1.IntSum;
import io.opentelemetry.proto.metrics.v1.Metric; import io.opentelemetry.proto.metrics.v1.Metric;
import io.opentelemetry.proto.metrics.v1.ResourceMetrics; import io.opentelemetry.proto.metrics.v1.ResourceMetrics;
@ -37,6 +38,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
final class MetricAdapter { final class MetricAdapter {
static List<ResourceMetrics> toProtoResourceMetrics(Collection<MetricData> metricData) { static List<ResourceMetrics> toProtoResourceMetrics(Collection<MetricData> metricData) {
Map<Resource, Map<InstrumentationLibraryInfo, List<Metric>>> resourceAndLibraryMap = Map<Resource, Map<InstrumentationLibraryInfo, List<Metric>>> resourceAndLibraryMap =
groupByResourceAndLibrary(metricData); groupByResourceAndLibrary(metricData);
@ -74,11 +76,9 @@ final class MetricAdapter {
libraryInfoListMap = new HashMap<>(); libraryInfoListMap = new HashMap<>();
result.put(resource, libraryInfoListMap); result.put(resource, libraryInfoListMap);
} }
List<Metric> metricList = libraryInfoListMap.get(metricData.getInstrumentationLibraryInfo()); List<Metric> metricList =
if (metricList == null) { libraryInfoListMap.computeIfAbsent(
metricList = new ArrayList<>(); metricData.getInstrumentationLibraryInfo(), k -> new ArrayList<>());
libraryInfoListMap.put(metricData.getInstrumentationLibraryInfo(), metricList);
}
metricList.add(toProtoMetric(metricData)); metricList.add(toProtoMetric(metricData));
} }
return result; return result;
@ -129,6 +129,18 @@ final class MetricAdapter {
.addAllDataPoints(toSummaryDataPoints(metricData.getPoints())) .addAllDataPoints(toSummaryDataPoints(metricData.getPoints()))
.build()); .build());
break; break;
case GAUGE_LONG:
builder.setIntGauge(
IntGauge.newBuilder()
.addAllDataPoints(toIntDataPoints(metricData.getPoints()))
.build());
break;
case GAUGE_DOUBLE:
builder.setDoubleGauge(
DoubleGauge.newBuilder()
.addAllDataPoints(toDoubleDataPoints(metricData.getPoints()))
.build());
break;
} }
return builder.build(); return builder.build();
} }
@ -142,8 +154,9 @@ final class MetricAdapter {
return AGGREGATION_TEMPORALITY_CUMULATIVE; return AGGREGATION_TEMPORALITY_CUMULATIVE;
case SUMMARY: case SUMMARY:
return AGGREGATION_TEMPORALITY_DELTA; return AGGREGATION_TEMPORALITY_DELTA;
default:
return AGGREGATION_TEMPORALITY_UNSPECIFIED;
} }
return AGGREGATION_TEMPORALITY_UNSPECIFIED;
} }
static List<IntDataPoint> toIntDataPoints(Collection<Point> points) { static List<IntDataPoint> toIntDataPoints(Collection<Point> points) {
@ -229,12 +242,8 @@ final class MetricAdapter {
} }
final List<StringKeyValue> result = new ArrayList<>(labels.size()); final List<StringKeyValue> result = new ArrayList<>(labels.size());
labels.forEach( labels.forEach(
new LabelConsumer() { (key, value) ->
@Override result.add(StringKeyValue.newBuilder().setKey(key).setValue(value).build()));
public void consume(String key, String value) {
result.add(StringKeyValue.newBuilder().setKey(key).setValue(value).build());
}
});
return result; return result;
} }

View File

@ -7,6 +7,8 @@ package io.opentelemetry.exporters.otlp;
import static io.opentelemetry.common.AttributeKey.stringKey; import static io.opentelemetry.common.AttributeKey.stringKey;
import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE; import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE;
import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -18,10 +20,13 @@ import io.opentelemetry.proto.common.v1.InstrumentationLibrary;
import io.opentelemetry.proto.common.v1.KeyValue; import io.opentelemetry.proto.common.v1.KeyValue;
import io.opentelemetry.proto.common.v1.StringKeyValue; import io.opentelemetry.proto.common.v1.StringKeyValue;
import io.opentelemetry.proto.metrics.v1.DoubleDataPoint; import io.opentelemetry.proto.metrics.v1.DoubleDataPoint;
import io.opentelemetry.proto.metrics.v1.DoubleGauge;
import io.opentelemetry.proto.metrics.v1.DoubleHistogram;
import io.opentelemetry.proto.metrics.v1.DoubleHistogramDataPoint; import io.opentelemetry.proto.metrics.v1.DoubleHistogramDataPoint;
import io.opentelemetry.proto.metrics.v1.DoubleSum; import io.opentelemetry.proto.metrics.v1.DoubleSum;
import io.opentelemetry.proto.metrics.v1.InstrumentationLibraryMetrics; import io.opentelemetry.proto.metrics.v1.InstrumentationLibraryMetrics;
import io.opentelemetry.proto.metrics.v1.IntDataPoint; import io.opentelemetry.proto.metrics.v1.IntDataPoint;
import io.opentelemetry.proto.metrics.v1.IntGauge;
import io.opentelemetry.proto.metrics.v1.IntSum; import io.opentelemetry.proto.metrics.v1.IntSum;
import io.opentelemetry.proto.metrics.v1.Metric; import io.opentelemetry.proto.metrics.v1.Metric;
import io.opentelemetry.proto.metrics.v1.ResourceMetrics; import io.opentelemetry.proto.metrics.v1.ResourceMetrics;
@ -196,7 +201,7 @@ class MetricAdapterTest {
} }
@Test @Test
void toProtoMetric() { void toProtoMetric_monotonic() {
assertThat( assertThat(
MetricAdapter.toProtoMetric( MetricAdapter.toProtoMetric(
MetricData.create( MetricData.create(
@ -266,6 +271,183 @@ class MetricAdapterTest {
.build()); .build());
} }
@Test
void toProtoMetric_nonMonotonic() {
assertThat(
MetricAdapter.toProtoMetric(
MetricData.create(
Resource.getEmpty(),
InstrumentationLibraryInfo.getEmpty(),
"name",
"description",
"1",
MetricData.Type.NON_MONOTONIC_LONG,
singletonList(MetricData.LongPoint.create(123, 456, Labels.of("k", "v"), 5)))))
.isEqualTo(
Metric.newBuilder()
.setName("name")
.setDescription("description")
.setUnit("1")
.setIntSum(
IntSum.newBuilder()
.setIsMonotonic(false)
.setAggregationTemporality(AGGREGATION_TEMPORALITY_CUMULATIVE)
.addDataPoints(
IntDataPoint.newBuilder()
.setStartTimeUnixNano(123)
.setTimeUnixNano(456)
.addAllLabels(
singletonList(
StringKeyValue.newBuilder()
.setKey("k")
.setValue("v")
.build()))
.setValue(5)
.build())
.build())
.build());
assertThat(
MetricAdapter.toProtoMetric(
MetricData.create(
Resource.getEmpty(),
InstrumentationLibraryInfo.getEmpty(),
"name",
"description",
"1",
MetricData.Type.NON_MONOTONIC_DOUBLE,
singletonList(
MetricData.DoublePoint.create(123, 456, Labels.of("k", "v"), 5.1)))))
.isEqualTo(
Metric.newBuilder()
.setName("name")
.setDescription("description")
.setUnit("1")
.setDoubleSum(
DoubleSum.newBuilder()
.setIsMonotonic(false)
.setAggregationTemporality(AGGREGATION_TEMPORALITY_CUMULATIVE)
.addDataPoints(
DoubleDataPoint.newBuilder()
.setStartTimeUnixNano(123)
.setTimeUnixNano(456)
.addAllLabels(
singletonList(
StringKeyValue.newBuilder()
.setKey("k")
.setValue("v")
.build()))
.setValue(5.1)
.build())
.build())
.build());
}
@Test
void toProtoMetric_gauges() {
assertThat(
MetricAdapter.toProtoMetric(
MetricData.create(
Resource.getEmpty(),
InstrumentationLibraryInfo.getEmpty(),
"name",
"description",
"1",
MetricData.Type.GAUGE_LONG,
singletonList(MetricData.LongPoint.create(123, 456, Labels.of("k", "v"), 5)))))
.isEqualTo(
Metric.newBuilder()
.setName("name")
.setDescription("description")
.setUnit("1")
.setIntGauge(
IntGauge.newBuilder()
.addDataPoints(
IntDataPoint.newBuilder()
.setStartTimeUnixNano(123)
.setTimeUnixNano(456)
.addAllLabels(
singletonList(
StringKeyValue.newBuilder()
.setKey("k")
.setValue("v")
.build()))
.setValue(5)
.build())
.build())
.build());
assertThat(
MetricAdapter.toProtoMetric(
MetricData.create(
Resource.getEmpty(),
InstrumentationLibraryInfo.getEmpty(),
"name",
"description",
"1",
MetricData.Type.GAUGE_DOUBLE,
singletonList(
MetricData.DoublePoint.create(123, 456, Labels.of("k", "v"), 5.1)))))
.isEqualTo(
Metric.newBuilder()
.setName("name")
.setDescription("description")
.setUnit("1")
.setDoubleGauge(
DoubleGauge.newBuilder()
.addDataPoints(
DoubleDataPoint.newBuilder()
.setStartTimeUnixNano(123)
.setTimeUnixNano(456)
.addAllLabels(
singletonList(
StringKeyValue.newBuilder()
.setKey("k")
.setValue("v")
.build()))
.setValue(5.1)
.build())
.build())
.build());
}
@Test
void toProtoMetric_summary() {
assertThat(
MetricAdapter.toProtoMetric(
MetricData.create(
Resource.getEmpty(),
InstrumentationLibraryInfo.getEmpty(),
"name",
"description",
"1",
MetricData.Type.SUMMARY,
singletonList(
MetricData.SummaryPoint.create(
123, 456, Labels.of("k", "v"), 5, 33d, emptyList())))))
.isEqualTo(
Metric.newBuilder()
.setName("name")
.setDescription("description")
.setUnit("1")
.setDoubleHistogram(
DoubleHistogram.newBuilder()
.setAggregationTemporality(AGGREGATION_TEMPORALITY_DELTA)
.addDataPoints(
DoubleHistogramDataPoint.newBuilder()
.setStartTimeUnixNano(123)
.setTimeUnixNano(456)
.addAllLabels(
singletonList(
StringKeyValue.newBuilder()
.setKey("k")
.setValue("v")
.build()))
.setCount(5)
.setSum(33d)
.build())
.build())
.build());
}
@Test @Test
void toProtoResourceMetrics() { void toProtoResourceMetrics() {
Resource resource = Resource.create(Attributes.of(stringKey("ka"), "va")); Resource resource = Resource.create(Attributes.of(stringKey("ka"), "va"));

View File

@ -63,6 +63,8 @@ final class MetricAdapter {
switch (type) { switch (type) {
case NON_MONOTONIC_LONG: case NON_MONOTONIC_LONG:
case NON_MONOTONIC_DOUBLE: case NON_MONOTONIC_DOUBLE:
case GAUGE_LONG:
case GAUGE_DOUBLE:
return Collector.Type.GAUGE; return Collector.Type.GAUGE;
case MONOTONIC_LONG: case MONOTONIC_LONG:
case MONOTONIC_DOUBLE: case MONOTONIC_DOUBLE:
@ -92,11 +94,13 @@ final class MetricAdapter {
switch (type) { switch (type) {
case MONOTONIC_DOUBLE: case MONOTONIC_DOUBLE:
case NON_MONOTONIC_DOUBLE: case NON_MONOTONIC_DOUBLE:
case GAUGE_DOUBLE:
DoublePoint doublePoint = (DoublePoint) point; DoublePoint doublePoint = (DoublePoint) point;
samples.add(new Sample(name, labelNames, labelValues, doublePoint.getValue())); samples.add(new Sample(name, labelNames, labelValues, doublePoint.getValue()));
break; break;
case MONOTONIC_LONG: case MONOTONIC_LONG:
case NON_MONOTONIC_LONG: case NON_MONOTONIC_LONG:
case GAUGE_LONG:
LongPoint longPoint = (LongPoint) point; LongPoint longPoint = (LongPoint) point;
samples.add(new Sample(name, labelNames, labelValues, longPoint.getValue())); samples.add(new Sample(name, labelNames, labelValues, longPoint.getValue()));
break; break;
@ -154,15 +158,9 @@ final class MetricAdapter {
} }
private static int estimateNumSamples(int numPoints, MetricData.Type type) { private static int estimateNumSamples(int numPoints, MetricData.Type type) {
switch (type) { if (type == MetricData.Type.SUMMARY) {
case NON_MONOTONIC_LONG: // count + sum + estimated 2 percentiles (default MinMaxSumCount aggregator).
case NON_MONOTONIC_DOUBLE: return numPoints * 4;
case MONOTONIC_LONG:
case MONOTONIC_DOUBLE:
return numPoints;
case SUMMARY:
// count + sum + estimated 2 percentiles (default MinMaxSumCount aggregator).
return numPoints * 4;
} }
return numPoints; return numPoints;
} }

View File

@ -35,6 +35,10 @@ class MetricAdapterTest {
.isEqualTo(Collector.Type.COUNTER); .isEqualTo(Collector.Type.COUNTER);
assertThat(MetricAdapter.toMetricFamilyType(MetricData.Type.SUMMARY)) assertThat(MetricAdapter.toMetricFamilyType(MetricData.Type.SUMMARY))
.isEqualTo(Collector.Type.SUMMARY); .isEqualTo(Collector.Type.SUMMARY);
assertThat(MetricAdapter.toMetricFamilyType(MetricData.Type.GAUGE_DOUBLE))
.isEqualTo(Collector.Type.GAUGE);
assertThat(MetricAdapter.toMetricFamilyType(MetricData.Type.GAUGE_LONG))
.isEqualTo(Collector.Type.GAUGE);
} }
@Test @Test
@ -72,6 +76,17 @@ class MetricAdapterTest {
.containsExactly( .containsExactly(
new Sample("full_name", Collections.emptyList(), Collections.emptyList(), 5), new Sample("full_name", Collections.emptyList(), Collections.emptyList(), 5),
new Sample("full_name", ImmutableList.of("kp"), ImmutableList.of("vp"), 7)); new Sample("full_name", ImmutableList.of("kp"), ImmutableList.of("vp"), 7));
assertThat(
MetricAdapter.toSamples(
"full_name",
MetricData.Type.GAUGE_LONG,
ImmutableList.of(
MetricData.LongPoint.create(123, 456, Labels.empty(), 5),
MetricData.LongPoint.create(321, 654, Labels.of("kp", "vp"), 7))))
.containsExactly(
new Sample("full_name", Collections.emptyList(), Collections.emptyList(), 5),
new Sample("full_name", ImmutableList.of("kp"), ImmutableList.of("vp"), 7));
} }
@Test @Test
@ -100,6 +115,17 @@ class MetricAdapterTest {
.containsExactly( .containsExactly(
new Sample("full_name", Collections.emptyList(), Collections.emptyList(), 5), new Sample("full_name", Collections.emptyList(), Collections.emptyList(), 5),
new Sample("full_name", ImmutableList.of("kp"), ImmutableList.of("vp"), 7)); new Sample("full_name", ImmutableList.of("kp"), ImmutableList.of("vp"), 7));
assertThat(
MetricAdapter.toSamples(
"full_name",
MetricData.Type.GAUGE_DOUBLE,
ImmutableList.of(
MetricData.DoublePoint.create(123, 456, Labels.empty(), 5),
MetricData.DoublePoint.create(321, 654, Labels.of("kp", "vp"), 7))))
.containsExactly(
new Sample("full_name", Collections.emptyList(), Collections.emptyList(), 5),
new Sample("full_name", ImmutableList.of("kp"), ImmutableList.of("vp"), 7));
} }
@Test @Test

View File

@ -19,6 +19,7 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@AutoValue @AutoValue
public abstract class MetricData { public abstract class MetricData {
MetricData() {} MetricData() {}
/** The kind of metric. It describes how the data is reported. */ /** The kind of metric. It describes how the data is reported. */
@ -41,6 +42,18 @@ public abstract class MetricData {
* recorded. * recorded.
*/ */
SUMMARY, SUMMARY,
/**
* A Gauge represents a measurement of a long value at a moment in time. Generally only one
* instance of a given Gauge metric will be reported per reporting interval.
*/
GAUGE_LONG,
/**
* A Gauge represents a measurement of a double value at a moment in time. Generally only one
* instance of a given Gauge metric will be reported per reporting interval.
*/
GAUGE_DOUBLE
} }
/** /**
@ -110,6 +123,7 @@ public abstract class MetricData {
@Immutable @Immutable
public abstract static class Point { public abstract static class Point {
Point() {} Point() {}
/** /**
@ -145,6 +159,7 @@ public abstract class MetricData {
@Immutable @Immutable
@AutoValue @AutoValue
public abstract static class LongPoint extends Point { public abstract static class LongPoint extends Point {
LongPoint() {} LongPoint() {}
/** /**
@ -167,6 +182,7 @@ public abstract class MetricData {
@Immutable @Immutable
@AutoValue @AutoValue
public abstract static class DoublePoint extends Point { public abstract static class DoublePoint extends Point {
DoublePoint() {} DoublePoint() {}
/** /**
@ -229,6 +245,7 @@ public abstract class MetricData {
@Immutable @Immutable
@AutoValue @AutoValue
public abstract static class ValueAtPercentile { public abstract static class ValueAtPercentile {
ValueAtPercentile() {} ValueAtPercentile() {}
/** /**

View File

@ -222,11 +222,14 @@ public class Aggregations {
return instrumentValueType == InstrumentValueType.LONG return instrumentValueType == InstrumentValueType.LONG
? MetricData.Type.MONOTONIC_LONG ? MetricData.Type.MONOTONIC_LONG
: MetricData.Type.MONOTONIC_DOUBLE; : MetricData.Type.MONOTONIC_DOUBLE;
case VALUE_OBSERVER:
case UP_DOWN_SUM_OBSERVER: case UP_DOWN_SUM_OBSERVER:
return instrumentValueType == InstrumentValueType.LONG return instrumentValueType == InstrumentValueType.LONG
? MetricData.Type.NON_MONOTONIC_LONG ? MetricData.Type.NON_MONOTONIC_LONG
: MetricData.Type.NON_MONOTONIC_DOUBLE; : MetricData.Type.NON_MONOTONIC_DOUBLE;
case VALUE_OBSERVER:
return instrumentValueType == InstrumentValueType.LONG
? MetricData.Type.GAUGE_LONG
: MetricData.Type.GAUGE_DOUBLE;
default: default:
// Do not change this unless the limitations of the current LastValueAggregator are fixed. // Do not change this unless the limitations of the current LastValueAggregator are fixed.
throw new IllegalArgumentException( throw new IllegalArgumentException(

View File

@ -64,7 +64,7 @@ class DoubleValueObserverSdkTest {
"testObserver", "testObserver",
"My own DoubleValueObserver", "My own DoubleValueObserver",
"ms", "ms",
MetricData.Type.NON_MONOTONIC_DOUBLE, MetricData.Type.GAUGE_DOUBLE,
Collections.emptyList())); Collections.emptyList()));
} }
@ -82,7 +82,7 @@ class DoubleValueObserverSdkTest {
"testObserver", "testObserver",
"", "",
"1", "1",
MetricData.Type.NON_MONOTONIC_DOUBLE, MetricData.Type.GAUGE_DOUBLE,
Collections.singletonList( Collections.singletonList(
DoublePoint.create( DoublePoint.create(
testClock.now() - SECOND_NANOS, testClock.now() - SECOND_NANOS,
@ -98,7 +98,7 @@ class DoubleValueObserverSdkTest {
"testObserver", "testObserver",
"", "",
"1", "1",
MetricData.Type.NON_MONOTONIC_DOUBLE, MetricData.Type.GAUGE_DOUBLE,
Collections.singletonList( Collections.singletonList(
DoublePoint.create( DoublePoint.create(
testClock.now() - SECOND_NANOS, testClock.now() - SECOND_NANOS,

View File

@ -63,7 +63,7 @@ class LongValueObserverSdkTest {
"testObserver", "testObserver",
"My own LongValueObserver", "My own LongValueObserver",
"ms", "ms",
MetricData.Type.NON_MONOTONIC_LONG, MetricData.Type.GAUGE_LONG,
Collections.emptyList())); Collections.emptyList()));
} }
@ -81,7 +81,7 @@ class LongValueObserverSdkTest {
"testObserver", "testObserver",
"", "",
"1", "1",
MetricData.Type.NON_MONOTONIC_LONG, MetricData.Type.GAUGE_LONG,
Collections.singletonList( Collections.singletonList(
LongPoint.create( LongPoint.create(
testClock.now() - SECOND_NANOS, testClock.now() - SECOND_NANOS,
@ -97,7 +97,7 @@ class LongValueObserverSdkTest {
"testObserver", "testObserver",
"", "",
"1", "1",
MetricData.Type.NON_MONOTONIC_LONG, MetricData.Type.GAUGE_LONG,
Collections.singletonList( Collections.singletonList(
LongPoint.create( LongPoint.create(
testClock.now() - SECOND_NANOS, testClock.now() - SECOND_NANOS,