Skip to content

Commit

Permalink
Histogram Aggregation - core SDK - data (#2923)
Browse files Browse the repository at this point in the history
* add histogram related metric data types

* update doc of histogram boundaries to make it compatible with latest change of ot-proto

* remove unnecessary helper

* simplify the create interface, add doc and validations
  • Loading branch information
beanliu authored Feb 25, 2021
1 parent 1102efa commit f1c38ef
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ static Metric toProtoMetric(MetricData metricData) {
.addAllDataPoints(toDoubleDataPoints(doubleGaugeData.getPoints()))
.build());
break;
case HISTOGRAM:
// no-op, will add in the following PRs
break;
}
return builder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ static Collector.Type toMetricFamilyType(MetricData metricData) {
return Collector.Type.GAUGE;
case SUMMARY:
return Collector.Type.SUMMARY;
case HISTOGRAM:
return Collector.Type.HISTOGRAM;
}
return Collector.Type.UNKNOWN;
}
Expand Down Expand Up @@ -122,6 +124,9 @@ static List<Sample> toSamples(
addSummarySamples(
(DoubleSummaryPointData) pointData, name, labelNames, labelValues, samples);
break;
case HISTOGRAM:
// no-op, will add in the following PRs
break;
}
}
return samples;
Expand Down Expand Up @@ -189,6 +194,8 @@ private static Collection<? extends PointData> getPoints(MetricData metricData)
return metricData.getLongSumData().getPoints();
case SUMMARY:
return metricData.getDoubleSummaryData().getPoints();
case HISTOGRAM:
return metricData.getDoubleHistogramData().getPoints();
}
return Collections.emptyList();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.metrics.data;

import com.google.auto.value.AutoValue;
import java.util.Collection;
import javax.annotation.concurrent.Immutable;

@Immutable
@AutoValue
public abstract class DoubleHistogramData implements Data<DoubleHistogramPointData> {
DoubleHistogramData() {}

public static DoubleHistogramData create(
AggregationTemporality temporality, Collection<DoubleHistogramPointData> points) {
return new AutoValue_DoubleHistogramData(temporality, points);
}

/**
* Returns the {@code AggregationTemporality} of this metric,
*
* <p>AggregationTemporality describes if the aggregator reports delta changes since last report
* time, or cumulative changes since a fixed start time.
*
* @return the {@code AggregationTemporality} of this metric
*/
public abstract AggregationTemporality getAggregationTemporality();

@Override
public abstract Collection<DoubleHistogramPointData> getPoints();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.metrics.data;

import com.google.auto.value.AutoValue;
import io.opentelemetry.api.metrics.common.Labels;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.concurrent.Immutable;

/**
* DoubleHistogramPointData represents an approximate representation of the distribution of
* measurements.
*/
@Immutable
@AutoValue
public abstract class DoubleHistogramPointData implements PointData {
/**
* Creates a DoubleHistogramPointData. For a Histogram with N defined boundaries, there should be
* N+1 counts.
*
* @return a DoubleHistogramPointData.
* @throws IllegalArgumentException if the given boundaries/counts were invalid
*/
public static DoubleHistogramPointData create(
long startEpochNanos,
long epochNanos,
Labels labels,
double sum,
List<Double> boundaries,
List<Long> counts) {
if (counts.size() != boundaries.size() + 1) {
throw new IllegalArgumentException(
"invalid counts: size should be "
+ (boundaries.size() + 1)
+ " instead of "
+ counts.size());
}
if (!isStrictlyIncreasing(boundaries)) {
throw new IllegalArgumentException("invalid boundaries: " + boundaries);
}
if (!boundaries.isEmpty()
&& (boundaries.get(0).isInfinite() || boundaries.get(boundaries.size() - 1).isInfinite())) {
throw new IllegalArgumentException("invalid boundaries: contains explicit +/-Inf");
}

long totalCount = 0;
for (long c : counts) {
totalCount += c;
}
return new AutoValue_DoubleHistogramPointData(
startEpochNanos,
epochNanos,
labels,
sum,
totalCount,
Collections.unmodifiableList(new ArrayList<>(boundaries)),
Collections.unmodifiableList(new ArrayList<>(counts)));
}

DoubleHistogramPointData() {}

/**
* The sum of all measurements recorded.
*
* @return the sum of recorded measurements.
*/
public abstract double getSum();

/**
* The number of measurements taken.
*
* @return the count of recorded measurements.
*/
public abstract long getCount();

/**
* The bucket boundaries. For a Histogram with N defined boundaries, e.g, [x, y, z]. There are N+1
* counts: (-inf, x], (x, y], (y, z], (z, +inf).
*
* @return the read-only bucket boundaries in increasing order. <b>do not mutate</b> the returned
* object.
*/
public abstract List<Double> getBoundaries();

/**
* The counts in each bucket.
*
* @return the read-only counts in each bucket. <b>do not mutate</b> the returned object.
*/
public abstract List<Long> getCounts();

private static boolean isStrictlyIncreasing(List<Double> xs) {
for (int i = 0; i < xs.size() - 1; i++) {
if (xs.get(i).compareTo(xs.get(i + 1)) >= 0) {
return false;
}
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public abstract class MetricData {
/* isMonotonic= */ false, AggregationTemporality.CUMULATIVE, Collections.emptyList());
private static final DoubleSummaryData DEFAULT_DOUBLE_SUMMARY_DATA =
DoubleSummaryData.create(Collections.emptyList());
private static final DoubleHistogramData DEFAULT_DOUBLE_HISTOGRAM_DATA =
DoubleHistogramData.create(AggregationTemporality.CUMULATIVE, Collections.emptyList());

/**
* Returns a new MetricData wih a {@link MetricDataType#DOUBLE_GAUGE} type.
Expand Down Expand Up @@ -140,6 +142,28 @@ public static MetricData createDoubleSummary(
data);
}

/**
* Returns a new MetricData with a {@link MetricDataType#HISTOGRAM} type.
*
* @return a new MetricData wih a {@link MetricDataType#HISTOGRAM} type.
*/
public static MetricData createDoubleHistogram(
Resource resource,
InstrumentationLibraryInfo instrumentationLibraryInfo,
String name,
String description,
String unit,
DoubleHistogramData data) {
return new AutoValue_MetricData(
resource,
instrumentationLibraryInfo,
name,
description,
unit,
MetricDataType.HISTOGRAM,
data);
}

MetricData() {}

/**
Expand Down Expand Up @@ -265,4 +289,18 @@ public final DoubleSummaryData getDoubleSummaryData() {
}
return DEFAULT_DOUBLE_SUMMARY_DATA;
}

/**
* Returns the {@code DoubleHistogramData} if type is {@link MetricDataType#HISTOGRAM}, otherwise
* a default empty data.
*
* @return the {@code DoubleHistogramData} if type is {@link MetricDataType#HISTOGRAM}, otherwise
* a default empty data.
*/
public final DoubleHistogramData getDoubleHistogramData() {
if (getType() == MetricDataType.HISTOGRAM) {
return (DoubleHistogramData) getData();
}
return DEFAULT_DOUBLE_HISTOGRAM_DATA;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@ public enum MetricDataType {
* value recorded, the sum of all measurements and the total number of measurements recorded.
*/
SUMMARY,

/**
* A Histogram represents an approximate representation of the distribution of measurements
* recorded.
*/
HISTOGRAM,
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

import static org.assertj.core.api.Assertions.assertThat;

import com.google.common.collect.ImmutableList;
import io.opentelemetry.api.metrics.common.Labels;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.resources.Resource;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/** Unit tests for {@link io.opentelemetry.sdk.metrics.data.MetricData}. */
Expand Down Expand Up @@ -40,6 +42,14 @@ class MetricDataTest {
Arrays.asList(
ValueAtPercentile.create(0.0, DOUBLE_VALUE),
ValueAtPercentile.create(100, DOUBLE_VALUE)));
private static final DoubleHistogramPointData HISTOGRAM_POINT =
DoubleHistogramPointData.create(
START_EPOCH_NANOS,
EPOCH_NANOS,
Labels.of("key", "value"),
DOUBLE_VALUE,
ImmutableList.of(1.0),
ImmutableList.of(1L, 1L));

@Test
void metricData_Getters() {
Expand Down Expand Up @@ -146,6 +156,55 @@ void metricData_SummaryPoints() {
assertThat(metricData.getDoubleSummaryData().getPoints()).containsExactly(SUMMARY_POINT);
}

@Test
void metricData_HistogramPoints() {
assertThat(HISTOGRAM_POINT.getStartEpochNanos()).isEqualTo(START_EPOCH_NANOS);
assertThat(HISTOGRAM_POINT.getEpochNanos()).isEqualTo(EPOCH_NANOS);
assertThat(HISTOGRAM_POINT.getLabels().size()).isEqualTo(1);
assertThat(HISTOGRAM_POINT.getLabels().get("key")).isEqualTo("value");
assertThat(HISTOGRAM_POINT.getCount()).isEqualTo(2L);
assertThat(HISTOGRAM_POINT.getSum()).isEqualTo(DOUBLE_VALUE);
assertThat(HISTOGRAM_POINT.getBoundaries()).isEqualTo(ImmutableList.of(1.0));
assertThat(HISTOGRAM_POINT.getCounts()).isEqualTo(ImmutableList.of(1L, 1L));

MetricData metricData =
MetricData.createDoubleHistogram(
Resource.empty(),
InstrumentationLibraryInfo.empty(),
"metric_name",
"metric_description",
"ms",
DoubleHistogramData.create(
AggregationTemporality.DELTA, Collections.singleton(HISTOGRAM_POINT)));
assertThat(metricData.getDoubleHistogramData().getPoints()).containsExactly(HISTOGRAM_POINT);

Assertions.assertThrows(
IllegalArgumentException.class,
() ->
DoubleHistogramPointData.create(
0, 0, Labels.empty(), 0.0, ImmutableList.of(), ImmutableList.of()));
Assertions.assertThrows(
IllegalArgumentException.class,
() ->
DoubleHistogramPointData.create(
0,
0,
Labels.empty(),
0.0,
ImmutableList.of(1.0, 1.0),
ImmutableList.of(0L, 0L, 0L)));
Assertions.assertThrows(
IllegalArgumentException.class,
() ->
DoubleHistogramPointData.create(
0,
0,
Labels.empty(),
0.0,
ImmutableList.of(Double.NEGATIVE_INFINITY),
ImmutableList.of(0L, 0L)));
}

@Test
void metricData_GetDefault() {
MetricData metricData =
Expand All @@ -160,6 +219,7 @@ void metricData_GetDefault() {
assertThat(metricData.getLongGaugeData().getPoints()).isEmpty();
assertThat(metricData.getDoubleSumData().getPoints()).isEmpty();
assertThat(metricData.getLongGaugeData().getPoints()).isEmpty();
assertThat(metricData.getDoubleHistogramData().getPoints()).isEmpty();
assertThat(metricData.getDoubleSummaryData().getPoints()).containsExactly(SUMMARY_POINT);

metricData =
Expand All @@ -174,6 +234,7 @@ void metricData_GetDefault() {
assertThat(metricData.getLongGaugeData().getPoints()).isEmpty();
assertThat(metricData.getDoubleSumData().getPoints()).isEmpty();
assertThat(metricData.getLongGaugeData().getPoints()).isEmpty();
assertThat(metricData.getDoubleHistogramData().getPoints()).isEmpty();
assertThat(metricData.getDoubleSummaryData().getPoints()).isEmpty();
}
}

0 comments on commit f1c38ef

Please sign in to comment.