From bcfd159c58e80036bbbe1468bf305154d001982e Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Tue, 2 Oct 2018 16:44:12 -0400 Subject: Make metrics compatible with the current proto definition. (#1479) * Make metrics compatible with the current proto definition. * Fix minor comments from the review. * Fix @Nullable. --- .../java/io/opencensus/metrics/Distribution.java | 24 +-- .../main/java/io/opencensus/metrics/Metric.java | 6 + .../io/opencensus/metrics/MetricDescriptor.java | 24 ++- .../main/java/io/opencensus/metrics/Summary.java | 187 ++++++++++++++++++++ api/src/main/java/io/opencensus/metrics/Value.java | 54 +++++- .../io/opencensus/metrics/DistributionTest.java | 29 ++-- .../test/java/io/opencensus/metrics/PointTest.java | 2 +- .../java/io/opencensus/metrics/SummaryTest.java | 189 +++++++++++++++++++++ .../test/java/io/opencensus/metrics/ValueTest.java | 35 +++- .../implcore/stats/MutableAggregation.java | 6 +- 10 files changed, 521 insertions(+), 35 deletions(-) create mode 100644 api/src/main/java/io/opencensus/metrics/Summary.java create mode 100644 api/src/test/java/io/opencensus/metrics/SummaryTest.java diff --git a/api/src/main/java/io/opencensus/metrics/Distribution.java b/api/src/main/java/io/opencensus/metrics/Distribution.java index 87f99635..9294b838 100644 --- a/api/src/main/java/io/opencensus/metrics/Distribution.java +++ b/api/src/main/java/io/opencensus/metrics/Distribution.java @@ -45,17 +45,17 @@ public abstract class Distribution { /** * Creates a {@link Distribution}. * - * @param mean mean of the population values. - * @param count count of the population values. - * @param sumOfSquaredDeviations sum of squared deviations of the population values. + * @param count the count of the population values. + * @param sum the sum of the population values. + * @param sumOfSquaredDeviations the sum of squared deviations of the population values. * @param bucketBoundaries bucket boundaries of a histogram. * @param buckets {@link Bucket}s of a histogram. * @return a {@code Distribution}. * @since 0.17 */ public static Distribution create( - double mean, long count, + double sum, double sumOfSquaredDeviations, List bucketBoundaries, List buckets) { @@ -63,13 +63,13 @@ public abstract class Distribution { Utils.checkArgument( sumOfSquaredDeviations >= 0, "sum of squared deviations should be non-negative."); if (count == 0) { - Utils.checkArgument(mean == 0, "mean should be 0 if count is 0."); + Utils.checkArgument(sum == 0, "sum should be 0 if count is 0."); Utils.checkArgument( sumOfSquaredDeviations == 0, "sum of squared deviations should be 0 if count is 0."); } return new AutoValue_Distribution( - mean, count, + sum, sumOfSquaredDeviations, copyBucketBounds(bucketBoundaries), copyBucketCount(buckets)); @@ -100,20 +100,20 @@ public abstract class Distribution { } /** - * Returns the aggregated mean. + * Returns the aggregated count. * - * @return the aggregated mean. + * @return the aggregated count. * @since 0.17 */ - public abstract double getMean(); + public abstract long getCount(); /** - * Returns the aggregated count. + * Returns the aggregated sum. * - * @return the aggregated count. + * @return the aggregated sum. * @since 0.17 */ - public abstract long getCount(); + public abstract double getSum(); /** * Returns the aggregated sum of squared deviations. diff --git a/api/src/main/java/io/opencensus/metrics/Metric.java b/api/src/main/java/io/opencensus/metrics/Metric.java index b463420c..0c108b56 100644 --- a/api/src/main/java/io/opencensus/metrics/Metric.java +++ b/api/src/main/java/io/opencensus/metrics/Metric.java @@ -22,6 +22,7 @@ import io.opencensus.internal.Utils; import io.opencensus.metrics.Value.ValueDistribution; import io.opencensus.metrics.Value.ValueDouble; import io.opencensus.metrics.Value.ValueLong; +import io.opencensus.metrics.Value.ValueSummary; import java.util.List; import javax.annotation.concurrent.Immutable; @@ -88,9 +89,14 @@ public abstract class Metric { Utils.checkArgument( value instanceof ValueDouble, "Type mismatch: %s, %s.", type, valueClassName); break; + case GAUGE_DISTRIBUTION: case CUMULATIVE_DISTRIBUTION: Utils.checkArgument( value instanceof ValueDistribution, "Type mismatch: %s, %s.", type, valueClassName); + break; + case SUMMARY: + Utils.checkArgument( + value instanceof ValueSummary, "Type mismatch: %s, %s.", type, valueClassName); } } } diff --git a/api/src/main/java/io/opencensus/metrics/MetricDescriptor.java b/api/src/main/java/io/opencensus/metrics/MetricDescriptor.java index e20730e9..5146cd85 100644 --- a/api/src/main/java/io/opencensus/metrics/MetricDescriptor.java +++ b/api/src/main/java/io/opencensus/metrics/MetricDescriptor.java @@ -126,6 +126,15 @@ public abstract class MetricDescriptor { */ GAUGE_DOUBLE, + /** + * An instantaneous measurement of a distribution value. The count and sum can go both up and + * down. Used in scenarios like a snapshot of time the current items in a queue have spent + * there. + * + * @since 0.17 + */ + GAUGE_DISTRIBUTION, + /** * An cumulative measurement of an int64 value. * @@ -141,10 +150,23 @@ public abstract class MetricDescriptor { CUMULATIVE_DOUBLE, /** - * An cumulative measurement of a distribution value. + * An cumulative measurement of a distribution value. The count and sum can only go up, if + * resets then the start_time should also be reset. * * @since 0.17 */ CUMULATIVE_DISTRIBUTION, + + /** + * Some frameworks implemented DISTRIBUTION as a summary of observations (usually things like + * request durations and response sizes). While it also provides a total count of observations + * and a sum of all observed values, it calculates configurable quantiles over a sliding time + * window. + * + *

This is not recommended, since it cannot be aggregated. + * + * @since 0.17 + */ + SUMMARY, } } diff --git a/api/src/main/java/io/opencensus/metrics/Summary.java b/api/src/main/java/io/opencensus/metrics/Summary.java new file mode 100644 index 00000000..f23667bb --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/Summary.java @@ -0,0 +1,187 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opencensus.metrics; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import io.opencensus.internal.Utils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Implementation of the {@link Distribution} as a summary of observations. + * + *

This is not recommended, since it cannot be aggregated. + * + * @since 0.17 + */ +@ExperimentalApi +@AutoValue +@Immutable +public abstract class Summary { + Summary() {} + + /** + * Creates a {@link Summary}. + * + * @param count the count of the population values. + * @param sum the sum of the population values. + * @param snapshot bucket boundaries of a histogram. + * @return a {@code Summary} with the given values. + * @since 0.17 + */ + public static Summary create(@Nullable Long count, @Nullable Double sum, Snapshot snapshot) { + checkCountAndSum(count, sum); + Utils.checkNotNull(snapshot, "snapshot"); + return new AutoValue_Summary(count, sum, snapshot); + } + + /** + * Returns the aggregated count. If not available returns {@code null}. + * + * @return the aggregated count. + * @since 0.17 + */ + @Nullable + public abstract Long getCount(); + + /** + * Returns the aggregated sum. If not available returns {@code null}. + * + * @return the aggregated sum. + * @since 0.17 + */ + @Nullable + public abstract Double getSum(); + + /** + * Returns the {@link Snapshot}. + * + * @return the {@code Snapshot}. + * @since 0.17 + */ + public abstract Snapshot getSnapshot(); + + /** + * Represents the summary observation of the recorded events over a sliding time window. + * + * @since 0.17 + */ + @Immutable + @AutoValue + public abstract static class Snapshot { + /** + * Returns the number of values in this {@code Snapshot}. If not available returns {@code null}. + * + * @return the number of values in this {@code Snapshot}. + * @since 0.17 + */ + @Nullable + public abstract Long getCount(); + + /** + * Returns the sum of values in this {@code Snapshot}. If not available returns {@code null}. + * + * @return the sum of values in this {@code Snapshot}. + * @since 0.17 + */ + @Nullable + public abstract Double getSum(); + + /** + * Returns the list of {@code ValueAtPercentile}s in this {@code Snapshot}. + * + * @return the list of {@code ValueAtPercentile}s in this {@code Snapshot}. + * @since 0.17 + */ + public abstract List getValueAtPercentiles(); + + /** + * Creates a {@link Snapshot}. + * + * @param count the number of values in this {@code Snapshot}. + * @param sum the number of values in this {@code Snapshot}. + * @param valueAtPercentiles the list of {@code ValueAtPercentile}. + * @return a {@code Snapshot} with the given values. + * @since 0.17 + */ + public static Snapshot create( + @Nullable Long count, @Nullable Double sum, List valueAtPercentiles) { + checkCountAndSum(count, sum); + Utils.checkNotNull(valueAtPercentiles, "valueAtPercentiles"); + Utils.checkListElementNotNull(valueAtPercentiles, "value in valueAtPercentiles"); + return new AutoValue_Summary_Snapshot( + count, + sum, + Collections.unmodifiableList(new ArrayList(valueAtPercentiles))); + } + + /** + * Represents the value at a given percentile of a distribution. + * + * @since 0.17 + */ + @Immutable + @AutoValue + public abstract static class ValueAtPercentile { + /** + * Returns the percentile in this {@code ValueAtPercentile}. + * + *

Must be in the interval (0.0, 100.0]. + * + * @return the percentile in this {@code ValueAtPercentile}. + * @since 0.17 + */ + public abstract double getPercentile(); + + /** + * Returns the value in this {@code ValueAtPercentile}. + * + * @return the value in this {@code ValueAtPercentile}. + * @since 0.17 + */ + public abstract double getValue(); + + /** + * Creates a {@link ValueAtPercentile}. + * + * @param percentile the percentile in this {@code ValueAtPercentile}. + * @param value the value in this {@code ValueAtPercentile}. + * @return a {@code ValueAtPercentile} with the given values. + * @since 0.17 + */ + public static ValueAtPercentile create(double percentile, double value) { + Utils.checkArgument( + 0 < percentile && percentile <= 100.0, + "percentile must be in the interval (0.0, 100.0]"); + Utils.checkArgument(value >= 0, "value must be non-negative"); + return new AutoValue_Summary_Snapshot_ValueAtPercentile(percentile, value); + } + } + } + + private static void checkCountAndSum(@Nullable Long count, @Nullable Double sum) { + Utils.checkArgument(count == null || count >= 0, "count must be non-negative."); + Utils.checkArgument(sum == null || sum >= 0, "sum must be non-negative."); + if (count != null && count == 0) { + Utils.checkArgument(sum == null || sum == 0, "sum must be 0 if count is 0."); + } + } +} diff --git a/api/src/main/java/io/opencensus/metrics/Value.java b/api/src/main/java/io/opencensus/metrics/Value.java index 5dd70c8d..1bc63b79 100644 --- a/api/src/main/java/io/opencensus/metrics/Value.java +++ b/api/src/main/java/io/opencensus/metrics/Value.java @@ -75,6 +75,17 @@ public abstract class Value { return ValueDistribution.create(value); } + /** + * Returns a {@link Summary} {@link Value}. + * + * @param value value in {@link Summary}. + * @return a {@code Summary} {@code Value}. + * @since 0.17 + */ + public static Value summaryValue(Summary value) { + return ValueSummary.create(value); + } + /** * Applies the given match function to the underlying data type. * @@ -84,6 +95,7 @@ public abstract class Value { Function doubleFunction, Function longFunction, Function distributionFunction, + Function summaryFunction, Function defaultFunction); /** A 64-bit double-precision floating-point {@link Value}. */ @@ -98,6 +110,7 @@ public abstract class Value { Function doubleFunction, Function longFunction, Function distributionFunction, + Function summaryFunction, Function defaultFunction) { return doubleFunction.apply(getValue()); } @@ -132,6 +145,7 @@ public abstract class Value { Function doubleFunction, Function longFunction, Function distributionFunction, + Function summaryFunction, Function defaultFunction) { return longFunction.apply(getValue()); } @@ -169,6 +183,7 @@ public abstract class Value { Function doubleFunction, Function longFunction, Function distributionFunction, + Function summaryFunction, Function defaultFunction) { return distributionFunction.apply(getValue()); } @@ -191,6 +206,41 @@ public abstract class Value { abstract Distribution getValue(); } - // TODO(songya): Add support for Summary type. - // This is an aggregation that produces percentiles directly. + /** + * {@link ValueSummary} contains a snapshot representing values calculated over an arbitrary time + * window. + */ + @AutoValue + @Immutable + abstract static class ValueSummary extends Value { + + ValueSummary() {} + + @Override + public final T match( + Function doubleFunction, + Function longFunction, + Function distributionFunction, + Function summaryFunction, + Function defaultFunction) { + return summaryFunction.apply(getValue()); + } + + /** + * Creates a {@link ValueSummary}. + * + * @param value the {@link Summary} value. + * @return a {@code ValueSummary}. + */ + static ValueSummary create(Summary value) { + return new AutoValue_Value_ValueSummary(value); + } + + /** + * Returns the {@link Summary} value. + * + * @return the {@code Summary} value. + */ + abstract Summary getValue(); + } } diff --git a/api/src/test/java/io/opencensus/metrics/DistributionTest.java b/api/src/test/java/io/opencensus/metrics/DistributionTest.java index d511e317..5ee88dd6 100644 --- a/api/src/test/java/io/opencensus/metrics/DistributionTest.java +++ b/api/src/test/java/io/opencensus/metrics/DistributionTest.java @@ -32,7 +32,7 @@ import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Unit tests for {@link Value}. */ +/** Unit tests for {@link Distribution}. */ @RunWith(JUnit4.class) public class DistributionTest { @@ -40,6 +40,7 @@ public class DistributionTest { private static final Timestamp TIMESTAMP = Timestamp.create(1, 0); private static final Map ATTACHMENTS = Collections.singletonMap("key", "value"); + private static final double TOLERANCE = 1e-6; @Test public void createAndGet_Bucket() { @@ -66,7 +67,7 @@ public class DistributionTest { @Test public void createAndGet_Exemplar() { Exemplar exemplar = Exemplar.create(-9.9, TIMESTAMP, ATTACHMENTS); - assertThat(exemplar.getValue()).isEqualTo(-9.9); + assertThat(exemplar.getValue()).isWithin(TOLERANCE).of(-9.9); assertThat(exemplar.getTimestamp()).isEqualTo(TIMESTAMP); assertThat(exemplar.getAttachments()).isEqualTo(ATTACHMENTS); } @@ -78,10 +79,10 @@ public class DistributionTest { List buckets = Arrays.asList( Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4, exemplar)); - Distribution distribution = Distribution.create(6.6, 10, 678.54, bucketBounds, buckets); - assertThat(distribution.getMean()).isEqualTo(6.6); + Distribution distribution = Distribution.create(10, 6.6, 678.54, bucketBounds, buckets); assertThat(distribution.getCount()).isEqualTo(10); - assertThat(distribution.getSumOfSquaredDeviations()).isEqualTo(678.54); + assertThat(distribution.getSum()).isWithin(TOLERANCE).of(6.6); + assertThat(distribution.getSumOfSquaredDeviations()).isWithin(TOLERANCE).of(678.54); assertThat(distribution.getBucketBoundaries()) .containsExactlyElementsIn(bucketBounds) .inOrder(); @@ -125,7 +126,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, bucketBounds, buckets); + Distribution.create(-10, 6.6, 678.54, bucketBounds, buckets); } @Test @@ -135,7 +136,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, bucketBounds, buckets); + Distribution.create(0, 6.6, -678.54, bucketBounds, buckets); } @Test @@ -144,8 +145,8 @@ public class DistributionTest { List buckets = 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, bucketBounds, buckets); + thrown.expectMessage("sum should be 0 if count is 0."); + Distribution.create(0, 6.6, 0, bucketBounds, buckets); } @Test @@ -164,7 +165,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, null, buckets); + Distribution.create(10, 6.6, 678.54, null, buckets); } @Test @@ -174,7 +175,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, bucketBounds, buckets); + Distribution.create(10, 6.6, 678.54, bucketBounds, buckets); } @Test @@ -182,7 +183,7 @@ public class DistributionTest { List 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, bucketBounds, null); + Distribution.create(10, 6.6, 678.54, bucketBounds, null); } @Test @@ -192,7 +193,7 @@ 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, bucketBounds, buckets); + Distribution.create(10, 6.6, 678.54, bucketBounds, buckets); } @Test @@ -215,7 +216,7 @@ public class DistributionTest { Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)))) .addEqualityGroup( Distribution.create( - -7, + 7, 10, 23.456, Arrays.asList(-5.0, 0.0, 5.0), diff --git a/api/src/test/java/io/opencensus/metrics/PointTest.java b/api/src/test/java/io/opencensus/metrics/PointTest.java index cb6175c1..708814c5 100644 --- a/api/src/test/java/io/opencensus/metrics/PointTest.java +++ b/api/src/test/java/io/opencensus/metrics/PointTest.java @@ -35,8 +35,8 @@ public class PointTest { private static final Value DISTRIBUTION_VALUE = Value.distributionValue( Distribution.create( - 6.6, 10, + 6.6, 678.54, Arrays.asList(-1.0, 0.0, 1.0), Arrays.asList( diff --git a/api/src/test/java/io/opencensus/metrics/SummaryTest.java b/api/src/test/java/io/opencensus/metrics/SummaryTest.java new file mode 100644 index 00000000..0b70d94e --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/SummaryTest.java @@ -0,0 +1,189 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opencensus.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import io.opencensus.metrics.Summary.Snapshot; +import io.opencensus.metrics.Summary.Snapshot.ValueAtPercentile; +import java.util.Collections; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link Summary}. */ +@RunWith(JUnit4.class) +public class SummaryTest { + + @Rule public final ExpectedException thrown = ExpectedException.none(); + private static final double TOLERANCE = 1e-6; + + @Test + public void createAndGet_ValueAtPercentile() { + ValueAtPercentile valueAtPercentile = ValueAtPercentile.create(99.5, 10.2); + assertThat(valueAtPercentile.getPercentile()).isWithin(TOLERANCE).of(99.5); + assertThat(valueAtPercentile.getValue()).isWithin(TOLERANCE).of(10.2); + } + + @Test + public void createValueAtPercentile_InvalidValueAtPercentileInterval() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("percentile must be in the interval (0.0, 100.0]"); + ValueAtPercentile.create(100.1, 10.2); + } + + @Test + public void createValueAtPercentile_NegativeValue() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("value must be non-negative"); + ValueAtPercentile.create(99.5, -10.2); + } + + @Test + public void createAndGet_Snapshot() { + Snapshot snapshot = + Snapshot.create( + 10L, 87.07, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2))); + assertThat(snapshot.getCount()).isEqualTo(10); + assertThat(snapshot.getSum()).isWithin(TOLERANCE).of(87.07); + assertThat(snapshot.getValueAtPercentiles()) + .containsExactly(ValueAtPercentile.create(99.5, 10.2)); + } + + @Test + public void createAndGet_Snapshot_WithNullCountAndSum() { + Snapshot snapshot = + Snapshot.create( + null, null, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2))); + assertThat(snapshot.getCount()).isNull(); + assertThat(snapshot.getSum()).isNull(); + assertThat(snapshot.getValueAtPercentiles()) + .containsExactly(ValueAtPercentile.create(99.5, 10.2)); + } + + @Test + public void createSnapshot_NegativeCount() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("count must be non-negative"); + Snapshot.create(-10L, 87.07, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2))); + } + + @Test + public void createSnapshot_NegativeSum() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("sum must be non-negative"); + Snapshot.create(10L, -87.07, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2))); + } + + @Test + public void createSnapshot_ZeroCountAndNonZeroSum() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("sum must be 0 if count is 0"); + Snapshot.create(0L, 87.07, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2))); + } + + @Test + public void createSnapshot_NullValueAtPercentilesList() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("valueAtPercentiles"); + Snapshot.create(10L, 87.07, null); + } + + @Test + public void createSnapshot_OneNullValueAtPercentile() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("value in valueAtPercentiles"); + Snapshot.create(10L, 87.07, Collections.singletonList(null)); + } + + @Test + public void createAndGet_Summary() { + Snapshot snapshot = + Snapshot.create( + 10L, 87.07, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2))); + Summary summary = Summary.create(10L, 6.6, snapshot); + assertThat(summary.getCount()).isEqualTo(10); + assertThat(summary.getSum()).isWithin(TOLERANCE).of(6.6); + assertThat(summary.getSnapshot()).isEqualTo(snapshot); + } + + @Test + public void createSummary_NegativeCount() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("count must be non-negative"); + Summary.create( + -10L, 6.6, Snapshot.create(null, null, Collections.emptyList())); + } + + @Test + public void createSummary_NegativeSum() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("sum must be non-negative"); + Summary.create( + 10L, -6.6, Snapshot.create(null, null, Collections.emptyList())); + } + + @Test + public void createSummary_ZeroCountAndNonZeroSum() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("sum must be 0 if count is 0"); + Summary.create( + 0L, 6.6, Snapshot.create(null, null, Collections.emptyList())); + } + + @Test + public void createSummary_NullSnapshot() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("snapshot"); + Summary.create(10L, 6.6, null); + } + + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup( + Summary.create( + 10L, + 10.0, + Snapshot.create( + 10L, 87.07, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2)))), + Summary.create( + 10L, + 10.0, + Snapshot.create( + 10L, 87.07, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2))))) + .addEqualityGroup( + Summary.create( + 7L, + 10.0, + Snapshot.create( + 10L, 87.07, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2))))) + .addEqualityGroup( + Summary.create( + 10L, + 7.0, + Snapshot.create( + 10L, 87.07, Collections.singletonList(ValueAtPercentile.create(99.5, 10.2))))) + .addEqualityGroup( + Summary.create( + 10L, 10.0, Snapshot.create(null, null, Collections.emptyList()))) + .testEquals(); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/ValueTest.java b/api/src/test/java/io/opencensus/metrics/ValueTest.java index 63430b28..a65202a8 100644 --- a/api/src/test/java/io/opencensus/metrics/ValueTest.java +++ b/api/src/test/java/io/opencensus/metrics/ValueTest.java @@ -22,11 +22,15 @@ 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.Summary.Snapshot; +import io.opencensus.metrics.Summary.Snapshot.ValueAtPercentile; import io.opencensus.metrics.Value.ValueDistribution; import io.opencensus.metrics.Value.ValueDouble; import io.opencensus.metrics.Value.ValueLong; +import io.opencensus.metrics.Value.ValueSummary; 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; @@ -35,6 +39,7 @@ import org.junit.runners.JUnit4; /** Unit tests for {@link Value}. */ @RunWith(JUnit4.class) public class ValueTest { + private static final double TOLERANCE = 1e-6; private static final Distribution DISTRIBUTION = Distribution.create( @@ -43,12 +48,18 @@ public class ValueTest { 1, Arrays.asList(-5.0, 0.0, 5.0), Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4))); + private static final Summary SUMMARY = + Summary.create( + 10L, + 10.0, + Snapshot.create( + 10L, 87.07, Collections.singletonList(ValueAtPercentile.create(0.98, 10.2)))); @Test public void createAndGet_ValueDouble() { Value value = Value.doubleValue(-34.56); assertThat(value).isInstanceOf(ValueDouble.class); - assertThat(((ValueDouble) value).getValue()).isEqualTo(-34.56); + assertThat(((ValueDouble) value).getValue()).isWithin(TOLERANCE).of(-34.56); } @Test @@ -65,6 +76,13 @@ public class ValueTest { assertThat(((ValueDistribution) value).getValue()).isEqualTo(DISTRIBUTION); } + @Test + public void createAndGet_ValueSummary() { + Value value = Value.summaryValue(SUMMARY); + assertThat(value).isInstanceOf(ValueSummary.class); + assertThat(((ValueSummary) value).getValue()).isEqualTo(SUMMARY); + } + @Test public void testEquals() { new EqualsTester() @@ -75,7 +93,7 @@ public class ValueTest { .addEqualityGroup( Value.distributionValue( Distribution.create( - -7, + 7, 10, 23.456, Arrays.asList(-5.0, 0.0, 5.0), @@ -88,7 +106,10 @@ public class ValueTest { public void testMatch() { List values = Arrays.asList( - ValueDouble.create(1.0), ValueLong.create(-1), ValueDistribution.create(DISTRIBUTION)); + ValueDouble.create(1.0), + ValueLong.create(-1), + ValueDistribution.create(DISTRIBUTION), + ValueSummary.create(SUMMARY)); List expected = Arrays.asList(1.0, -1L, 10.0, 10L, 1.0, -5.0, 0.0, 5.0, 3L, 1L, 2L, 4L); final List actual = new ArrayList(); @@ -111,7 +132,7 @@ public class ValueTest { new Function() { @Override public Object apply(Distribution arg) { - actual.add(arg.getMean()); + actual.add(arg.getSum()); actual.add(arg.getCount()); actual.add(arg.getSumOfSquaredDeviations()); actual.addAll(arg.getBucketBoundaries()); @@ -121,6 +142,12 @@ public class ValueTest { return null; } }, + new Function() { + @Override + public Object apply(Summary arg) { + return null; + } + }, Functions.throwAssertionError()); } assertThat(actual).containsExactlyElementsIn(expected).inOrder(); diff --git a/impl_core/src/main/java/io/opencensus/implcore/stats/MutableAggregation.java b/impl_core/src/main/java/io/opencensus/implcore/stats/MutableAggregation.java index a41850d2..eaf440ff 100644 --- a/impl_core/src/main/java/io/opencensus/implcore/stats/MutableAggregation.java +++ b/impl_core/src/main/java/io/opencensus/implcore/stats/MutableAggregation.java @@ -430,7 +430,11 @@ abstract class MutableAggregation { return Point.create( Value.distributionValue( Distribution.create( - mean, count, sumOfSquaredDeviations, bucketBoundaries.getBoundaries(), buckets)), + count, + mean * count, + sumOfSquaredDeviations, + bucketBoundaries.getBoundaries(), + buckets)), timestamp); } -- cgit v1.2.3