diff options
author | Yang Song <songy23@users.noreply.github.com> | 2018-07-18 14:12:44 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-18 14:12:44 -0700 |
commit | 26af97f39aaaf8699588cc8d97f76e1a450a2571 (patch) | |
tree | a0985a7370796cee4617f51200a31bcaef75d7a0 | |
parent | 7e9d4b5563650ea06f96e4130b27789caf776753 (diff) | |
download | opencensus-java-26af97f39aaaf8699588cc8d97f76e1a450a2571.tar.gz |
Metrics: Add Exemplar to Distribution. (#1321)
4 files changed, 181 insertions, 21 deletions
diff --git a/metrics/src/main/java/io/opencensus/metrics/Distribution.java b/metrics/src/main/java/io/opencensus/metrics/Distribution.java index a1026c83..86d8bae1 100644 --- a/metrics/src/main/java/io/opencensus/metrics/Distribution.java +++ b/metrics/src/main/java/io/opencensus/metrics/Distribution.java @@ -18,9 +18,13 @@ package io.opencensus.metrics; import com.google.auto.value.AutoValue; import io.opencensus.common.ExperimentalApi; +import io.opencensus.common.Timestamp; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -46,6 +50,7 @@ public abstract class Distribution { * @param range {@link Range} of the population values, or {@code null} if count is 0. * @param bucketBoundaries bucket boundaries of a histogram. * @param buckets {@link Bucket}s of a histogram. + * @param exemplars the exemplars associated with histogram buckets. * @return a {@code Distribution}. * @since 0.16 */ @@ -55,7 +60,8 @@ public abstract class Distribution { double sumOfSquaredDeviations, @Nullable Range range, List<Double> bucketBoundaries, - List<Bucket> buckets) { + List<Bucket> buckets, + List<Exemplar> exemplars) { Utils.checkArgument(count >= 0, "count should be non-negative."); Utils.checkArgument( sumOfSquaredDeviations >= 0, "sum of squared deviations should be non-negative."); @@ -67,13 +73,16 @@ public abstract class Distribution { } else { Utils.checkArgument(range != null, "range should be present if count is not 0."); } + Utils.checkNotNull(exemplars, "exemplar list should not be null."); + Utils.checkListElementNotNull(exemplars, "exemplar should not be null."); return new AutoValue_Distribution( mean, count, sumOfSquaredDeviations, range, copyBucketBounds(bucketBoundaries), - copyBucketCount(buckets)); + copyBucketCount(buckets), + Collections.<Exemplar>unmodifiableList(new ArrayList<Exemplar>(exemplars))); } private static List<Double> copyBucketBounds(List<Double> bucketBoundaries) { @@ -175,6 +184,14 @@ public abstract class Distribution { public abstract List<Bucket> getBuckets(); /** + * Returns the {@link Exemplar}s associated with histogram buckets. + * + * @return the {@code Exemplar}s associated with histogram buckets. + * @since 0.16 + */ + public abstract List<Exemplar> getExemplars(); + + /** * The range of the population values. * * @since 0.16 @@ -247,5 +264,61 @@ public abstract class Distribution { public abstract long getCount(); } - // TODO(songya): add support for exemplars. + /** + * An example point that may be used to annotate aggregated distribution values, associated with a + * histogram bucket. + * + * @since 0.16 + */ + @Immutable + @AutoValue + public abstract static class Exemplar { + + Exemplar() {} + + /** + * Returns value of the {@link Exemplar} point. + * + * @return value of the {@code Exemplar} point. + * @since 0.16 + */ + public abstract double getValue(); + + /** + * Returns the time that this {@link Exemplar}'s value was recorded. + * + * @return the time that this {@code Exemplar}'s value was recorded. + * @since 0.16 + */ + public abstract Timestamp getTimestamp(); + + /** + * Returns the contextual information about the example value, represented as a string map. + * + * @return the contextual information about the example value. + * @since 0.16 + */ + public abstract Map<String, String> getAttachments(); + + /** + * Creates an {@link Exemplar}. + * + * @param value value of the {@link Exemplar} point. + * @param timestamp the time that this {@code Exemplar}'s value was recorded. + * @param attachments the contextual information about the example value. + * @return an {@code Exemplar}. + * @since 0.16 + */ + public static Exemplar create( + double value, Timestamp timestamp, Map<String, String> attachments) { + Utils.checkNotNull(attachments, "attachments"); + Map<String, String> attachmentsCopy = + Collections.unmodifiableMap(new HashMap<String, String>(attachments)); + for (Entry<String, String> entry : attachmentsCopy.entrySet()) { + Utils.checkNotNull(entry.getKey(), "key of attachments"); + Utils.checkNotNull(entry.getValue(), "value of attachments"); + } + return new AutoValue_Distribution_Exemplar(value, timestamp, attachmentsCopy); + } + } } diff --git a/metrics/src/test/java/io/opencensus/metrics/DistributionTest.java b/metrics/src/test/java/io/opencensus/metrics/DistributionTest.java index 46e664d9..1734f9f5 100644 --- a/metrics/src/test/java/io/opencensus/metrics/DistributionTest.java +++ b/metrics/src/test/java/io/opencensus/metrics/DistributionTest.java @@ -19,10 +19,14 @@ package io.opencensus.metrics; import static com.google.common.truth.Truth.assertThat; import com.google.common.testing.EqualsTester; +import io.opencensus.common.Timestamp; import io.opencensus.metrics.Distribution.Bucket; +import io.opencensus.metrics.Distribution.Exemplar; import io.opencensus.metrics.Distribution.Range; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -35,6 +39,12 @@ public class DistributionTest { @Rule public final ExpectedException thrown = ExpectedException.none(); + private static final List<Bucket> EMPTY_BUCKET_LIST = Collections.<Bucket>emptyList(); + private static final List<Exemplar> EMPTY_EXEMPLAR_LIST = Collections.<Exemplar>emptyList(); + private static final Timestamp TIMESTAMP_1 = Timestamp.create(1, 0); + private static final Timestamp TIMESTAMP_2 = Timestamp.create(2, 0); + private static final Map<String, String> ATTACHMENTS = Collections.singletonMap("key", "value"); + @Test public void createAndGet_Range() { Range range = Range.create(-5.5, 6.6); @@ -49,12 +59,23 @@ public class DistributionTest { } @Test + public void createAndGet_Exemplar() { + Exemplar exemplar = Exemplar.create(-9.9, TIMESTAMP_1, ATTACHMENTS); + assertThat(exemplar.getValue()).isEqualTo(-9.9); + assertThat(exemplar.getTimestamp()).isEqualTo(TIMESTAMP_1); + assertThat(exemplar.getAttachments()).isEqualTo(ATTACHMENTS); + } + + @Test public void createAndGet_Distribution() { Range range = Range.create(-3.4, 5.6); + Exemplar exemplar = Exemplar.create(15.0, TIMESTAMP_1, ATTACHMENTS); List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); List<Bucket> buckets = Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)); - Distribution distribution = Distribution.create(6.6, 10, 678.54, range, bucketBounds, buckets); + Distribution distribution = + Distribution.create( + 6.6, 10, 678.54, range, bucketBounds, buckets, Collections.singletonList(exemplar)); assertThat(distribution.getMean()).isEqualTo(6.6); assertThat(distribution.getCount()).isEqualTo(10); assertThat(distribution.getSumOfSquaredDeviations()).isEqualTo(678.54); @@ -63,6 +84,7 @@ public class DistributionTest { .containsExactlyElementsIn(bucketBounds) .inOrder(); assertThat(distribution.getBuckets()).containsExactlyElementsIn(buckets).inOrder(); + assertThat(distribution.getExemplars()).containsExactly(exemplar); } @Test @@ -80,6 +102,29 @@ public class DistributionTest { } @Test + public void createExemplar_PreventNullAttachments() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("attachments"); + Exemplar.create(15, TIMESTAMP_1, null); + } + + @Test + public void createExemplar_PreventNullAttachmentKey() { + Map<String, String> attachments = Collections.singletonMap(null, "value"); + thrown.expect(NullPointerException.class); + thrown.expectMessage("key of attachment"); + Exemplar.create(15, TIMESTAMP_1, attachments); + } + + @Test + public void createExemplar_PreventNullAttachmentValue() { + Map<String, String> attachments = Collections.singletonMap("key", null); + thrown.expect(NullPointerException.class); + thrown.expectMessage("value of attachment"); + Exemplar.create(15, TIMESTAMP_1, attachments); + } + + @Test public void createDistribution_NegativeCount() { Range range = Range.create(-3.4, 5.6); List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); @@ -87,7 +132,7 @@ public class DistributionTest { Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)); thrown.expect(IllegalArgumentException.class); thrown.expectMessage("count should be non-negative."); - Distribution.create(6.6, -10, 678.54, range, bucketBounds, buckets); + Distribution.create(6.6, -10, 678.54, range, bucketBounds, buckets, EMPTY_EXEMPLAR_LIST); } @Test @@ -97,7 +142,7 @@ public class DistributionTest { Arrays.asList(Bucket.create(0), Bucket.create(0), Bucket.create(0), Bucket.create(0)); thrown.expect(IllegalArgumentException.class); thrown.expectMessage("sum of squared deviations should be non-negative."); - Distribution.create(6.6, 0, -678.54, null, bucketBounds, buckets); + Distribution.create(6.6, 0, -678.54, null, bucketBounds, buckets, EMPTY_EXEMPLAR_LIST); } @Test @@ -108,7 +153,7 @@ public class DistributionTest { Arrays.asList(Bucket.create(0), Bucket.create(0), Bucket.create(0), Bucket.create(0)); thrown.expect(IllegalArgumentException.class); thrown.expectMessage("range should not be present if count is 0."); - Distribution.create(0, 0, 0, range, bucketBounds, buckets); + Distribution.create(0, 0, 0, range, bucketBounds, buckets, EMPTY_EXEMPLAR_LIST); } @Test @@ -118,7 +163,7 @@ public class DistributionTest { Arrays.asList(Bucket.create(0), Bucket.create(0), Bucket.create(0), Bucket.create(0)); thrown.expect(IllegalArgumentException.class); thrown.expectMessage("mean should be 0 if count is 0."); - Distribution.create(6.6, 0, 0, null, bucketBounds, buckets); + Distribution.create(6.6, 0, 0, null, bucketBounds, buckets, EMPTY_EXEMPLAR_LIST); } @Test @@ -128,7 +173,7 @@ public class DistributionTest { Arrays.asList(Bucket.create(0), Bucket.create(0), Bucket.create(0), Bucket.create(0)); thrown.expect(IllegalArgumentException.class); thrown.expectMessage("sum of squared deviations should be 0 if count is 0."); - Distribution.create(0, 0, 678.54, null, bucketBounds, buckets); + Distribution.create(0, 0, 678.54, null, bucketBounds, buckets, EMPTY_EXEMPLAR_LIST); } @Test @@ -138,7 +183,7 @@ public class DistributionTest { Arrays.asList(Bucket.create(0), Bucket.create(1), Bucket.create(0), Bucket.create(0)); thrown.expect(IllegalArgumentException.class); thrown.expectMessage("range should be present if count is not 0."); - Distribution.create(6.6, 1, 0, null, bucketBounds, buckets); + Distribution.create(6.6, 1, 0, null, bucketBounds, buckets, EMPTY_EXEMPLAR_LIST); } @Test @@ -148,7 +193,7 @@ public class DistributionTest { Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)); thrown.expect(NullPointerException.class); thrown.expectMessage("bucketBoundaries list should not be null."); - Distribution.create(6.6, 10, 678.54, range, null, buckets); + Distribution.create(6.6, 10, 678.54, range, null, buckets, EMPTY_EXEMPLAR_LIST); } @Test @@ -159,7 +204,7 @@ public class DistributionTest { Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)); thrown.expect(IllegalArgumentException.class); thrown.expectMessage("bucket boundaries not sorted."); - Distribution.create(6.6, 10, 678.54, range, bucketBounds, buckets); + Distribution.create(6.6, 10, 678.54, range, bucketBounds, buckets, EMPTY_EXEMPLAR_LIST); } @Test @@ -168,7 +213,7 @@ public class DistributionTest { List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); thrown.expect(NullPointerException.class); thrown.expectMessage("bucket list should not be null."); - Distribution.create(6.6, 10, 678.54, range, bucketBounds, null); + Distribution.create(6.6, 10, 678.54, range, bucketBounds, null, EMPTY_EXEMPLAR_LIST); } @Test @@ -179,7 +224,30 @@ public class DistributionTest { Arrays.asList(Bucket.create(3), Bucket.create(1), null, Bucket.create(4)); thrown.expect(NullPointerException.class); thrown.expectMessage("bucket should not be null."); - Distribution.create(6.6, 10, 678.54, range, bucketBounds, buckets); + Distribution.create(6.6, 10, 678.54, range, bucketBounds, buckets, EMPTY_EXEMPLAR_LIST); + } + + @Test + public void preventNullExemplarList() { + Range range = Range.create(1, 1); + thrown.expect(NullPointerException.class); + thrown.expectMessage("exemplar list should not be null."); + Distribution.create(1, 1, 1, range, Collections.<Double>emptyList(), EMPTY_BUCKET_LIST, null); + } + + @Test + public void preventNullExemplar() { + Range range = Range.create(1, 1); + thrown.expect(NullPointerException.class); + thrown.expectMessage("exemplar should not be null."); + Distribution.create( + 1, + 1, + 1, + range, + Collections.<Double>emptyList(), + EMPTY_BUCKET_LIST, + Collections.<Exemplar>singletonList(null)); } @Test @@ -193,7 +261,8 @@ public class DistributionTest { Range.create(1, 5), Arrays.asList(-5.0, 0.0, 5.0), Arrays.asList( - Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4))), + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)), + EMPTY_EXEMPLAR_LIST), Distribution.create( 10, 10, @@ -201,7 +270,18 @@ public class DistributionTest { Range.create(1, 5), Arrays.asList(-5.0, 0.0, 5.0), Arrays.asList( - Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)))) + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)), + EMPTY_EXEMPLAR_LIST)) + .addEqualityGroup( + Distribution.create( + -7, + 10, + 23.456, + Range.create(-19.1, 19.2), + Arrays.asList(-5.0, 0.0, 5.0), + Arrays.asList( + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)), + EMPTY_EXEMPLAR_LIST)) .addEqualityGroup( Distribution.create( -7, @@ -210,7 +290,8 @@ public class DistributionTest { Range.create(-19.1, 19.2), Arrays.asList(-5.0, 0.0, 5.0), Arrays.asList( - Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)))) + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)), + Collections.singletonList(Exemplar.create(1.0, TIMESTAMP_2, ATTACHMENTS)))) .testEquals(); } } diff --git a/metrics/src/test/java/io/opencensus/metrics/PointTest.java b/metrics/src/test/java/io/opencensus/metrics/PointTest.java index 2da74d38..27feffd8 100644 --- a/metrics/src/test/java/io/opencensus/metrics/PointTest.java +++ b/metrics/src/test/java/io/opencensus/metrics/PointTest.java @@ -21,8 +21,10 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.testing.EqualsTester; import io.opencensus.common.Timestamp; import io.opencensus.metrics.Distribution.Bucket; +import io.opencensus.metrics.Distribution.Exemplar; import io.opencensus.metrics.Distribution.Range; import java.util.Arrays; +import java.util.Collections; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -41,8 +43,8 @@ public class PointTest { 678.54, Range.create(-3.4, 5.6), Arrays.asList(-1.0, 0.0, 1.0), - Arrays.asList( - Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)))); + Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)), + Collections.<Exemplar>emptyList())); private static final Timestamp TIMESTAMP_1 = Timestamp.create(1, 2); private static final Timestamp TIMESTAMP_2 = Timestamp.create(3, 4); private static final Timestamp TIMESTAMP_3 = Timestamp.create(5, 6); diff --git a/metrics/src/test/java/io/opencensus/metrics/ValueTest.java b/metrics/src/test/java/io/opencensus/metrics/ValueTest.java index 4864f72f..ee79fa67 100644 --- a/metrics/src/test/java/io/opencensus/metrics/ValueTest.java +++ b/metrics/src/test/java/io/opencensus/metrics/ValueTest.java @@ -22,12 +22,14 @@ import com.google.common.testing.EqualsTester; import io.opencensus.common.Function; import io.opencensus.common.Functions; import io.opencensus.metrics.Distribution.Bucket; +import io.opencensus.metrics.Distribution.Exemplar; import io.opencensus.metrics.Distribution.Range; import io.opencensus.metrics.Value.ValueDistribution; import io.opencensus.metrics.Value.ValueDouble; import io.opencensus.metrics.Value.ValueLong; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,7 +46,8 @@ public class ValueTest { 1, Range.create(1, 5), Arrays.asList(-5.0, 0.0, 5.0), - Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4))); + Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)), + Collections.<Exemplar>emptyList()); @Test public void createAndGet_ValueDouble() { @@ -83,7 +86,8 @@ public class ValueTest { Range.create(-19.1, 19.2), Arrays.asList(-5.0, 0.0, 5.0), Arrays.asList( - Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4))))) + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)), + Collections.<Exemplar>emptyList()))) .testEquals(); } |