/* * 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.export; import com.google.auto.value.AutoValue; import io.opencensus.common.ExperimentalApi; import io.opencensus.common.Function; import io.opencensus.common.Timestamp; import io.opencensus.internal.Utils; 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; /** * {@link Distribution} contains summary statistics for a population of values. It optionally * contains a histogram representing the distribution of those values across a set of buckets. * * @since 0.17 */ @ExperimentalApi @AutoValue @Immutable public abstract class Distribution { Distribution() {} /** * Creates a {@link Distribution}. * * @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 bucketOptions the bucket options used to create a histogram for the distribution. * @param buckets {@link Bucket}s of a histogram. * @return a {@code Distribution}. * @since 0.17 */ public static Distribution create( long count, double sum, double sumOfSquaredDeviations, BucketOptions bucketOptions, List buckets) { Utils.checkArgument(count >= 0, "count should be non-negative."); Utils.checkArgument( sumOfSquaredDeviations >= 0, "sum of squared deviations should be non-negative."); if (count == 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."); } Utils.checkNotNull(bucketOptions, "bucketOptions"); List bucketsCopy = Collections.unmodifiableList(new ArrayList(Utils.checkNotNull(buckets, "buckets"))); Utils.checkListElementNotNull(bucketsCopy, "bucket"); return new AutoValue_Distribution( count, sum, sumOfSquaredDeviations, bucketOptions, bucketsCopy); } /** * Returns the aggregated count. * * @return the aggregated count. * @since 0.17 */ public abstract long getCount(); /** * Returns the aggregated sum. * * @return the aggregated sum. * @since 0.17 */ public abstract double getSum(); /** * Returns the aggregated sum of squared deviations. * *

The sum of squared deviations from the mean of the values in the population. For values x_i * this is: * *

Sum[i=1..n]((x_i - mean)^2) * *

If count is zero then this field must be zero. * * @return the aggregated sum of squared deviations. * @since 0.17 */ public abstract double getSumOfSquaredDeviations(); /** * Returns bucket options used to create a histogram for the distribution. * * @return the {@code BucketOptions} associated with the {@code Distribution}, or {@code null} if * there isn't one. * @since 0.17 */ @Nullable public abstract BucketOptions getBucketOptions(); /** * Returns the aggregated histogram {@link Bucket}s. * * @return the aggregated histogram buckets. * @since 0.17 */ public abstract List getBuckets(); /** * The bucket options used to create a histogram for the distribution. * * @since 0.17 */ @Immutable public abstract static class BucketOptions { private BucketOptions() {} /** * Returns a {@link ExplicitOptions}. * *

The bucket boundaries for that histogram are described by bucket_bounds. This defines * size(bucket_bounds) + 1 (= N) buckets. The boundaries for bucket index i are: * *

    *
  • {@code [0, bucket_bounds[i]) for i == 0} *
  • {@code [bucket_bounds[i-1], bucket_bounds[i]) for 0 < i < N-1} *
  • {@code [bucket_bounds[i-1], +infinity) for i == N-1} *
* *

If bucket_bounds has no elements (zero size), then there is no histogram associated with * the Distribution. If bucket_bounds has only one element, there are no finite buckets, and * that single element is the common boundary of the overflow and underflow buckets. The values * must be monotonically increasing. * * @param bucketBoundaries the bucket boundaries of a distribution (given explicitly). The * values must be strictly increasing and should be positive values. * @return a {@code ExplicitOptions} {@code BucketOptions}. * @since 0.17 */ public static BucketOptions explicitOptions(List bucketBoundaries) { return ExplicitOptions.create(bucketBoundaries); } /** * Applies the given match function to the underlying BucketOptions. * * @param explicitFunction the function that should be applied if the BucketOptions has type * {@code ExplicitOptions}. * @param defaultFunction the function that should be applied if the BucketOptions has a type * that was added after this {@code match} method was added to the API. See {@link * io.opencensus.common.Functions} for some common functions for handling unknown types. * @return the result of the function applied to the underlying BucketOptions. * @since 0.17 */ public abstract T match( Function explicitFunction, Function defaultFunction); /** A Bucket with explicit bounds {@link BucketOptions}. */ @AutoValue @Immutable public abstract static class ExplicitOptions extends BucketOptions { ExplicitOptions() {} @Override public final T match( Function explicitFunction, Function defaultFunction) { return explicitFunction.apply(this); } /** * Creates a {@link ExplicitOptions}. * * @param bucketBoundaries the bucket boundaries of a distribution (given explicitly). The * values must be strictly increasing and should be positive. * @return a {@code ExplicitOptions}. * @since 0.17 */ private static ExplicitOptions create(List bucketBoundaries) { Utils.checkNotNull(bucketBoundaries, "bucketBoundaries"); List bucketBoundariesCopy = Collections.unmodifiableList(new ArrayList(bucketBoundaries)); checkBucketBoundsAreSorted(bucketBoundariesCopy); return new AutoValue_Distribution_BucketOptions_ExplicitOptions(bucketBoundariesCopy); } private static void checkBucketBoundsAreSorted(List bucketBoundaries) { if (bucketBoundaries.size() >= 1) { double previous = Utils.checkNotNull(bucketBoundaries.get(0), "bucketBoundary"); Utils.checkArgument(previous > 0, "bucket boundary should be > 0"); for (int i = 1; i < bucketBoundaries.size(); i++) { double next = Utils.checkNotNull(bucketBoundaries.get(i), "bucketBoundary"); Utils.checkArgument(previous < next, "bucket boundaries not sorted."); previous = next; } } } /** * Returns the bucket boundaries of this distribution. * * @return the bucket boundaries of this distribution. * @since 0.17 */ public abstract List getBucketBoundaries(); } } /** * The histogram bucket of the population values. * * @since 0.17 */ @AutoValue @Immutable public abstract static class Bucket { Bucket() {} /** * Creates a {@link Bucket}. * * @param count the number of values in each bucket of the histogram. * @return a {@code Bucket}. * @since 0.17 */ public static Bucket create(long count) { Utils.checkArgument(count >= 0, "bucket count should be non-negative."); return new AutoValue_Distribution_Bucket(count, null); } /** * Creates a {@link Bucket} with an {@link Exemplar}. * * @param count the number of values in each bucket of the histogram. * @param exemplar the {@code Exemplar} of this {@code Bucket}. * @return a {@code Bucket}. * @since 0.17 */ public static Bucket create(long count, Exemplar exemplar) { Utils.checkArgument(count >= 0, "bucket count should be non-negative."); Utils.checkNotNull(exemplar, "exemplar"); return new AutoValue_Distribution_Bucket(count, exemplar); } /** * Returns the number of values in each bucket of the histogram. * * @return the number of values in each bucket of the histogram. * @since 0.17 */ public abstract long getCount(); /** * Returns the {@link Exemplar} associated with the {@link Bucket}, or {@code null} if there * isn't one. * * @return the {@code Exemplar} associated with the {@code Bucket}, or {@code null} if there * isn't one. * @since 0.17 */ @Nullable public abstract Exemplar getExemplar(); } /** * An example point that may be used to annotate aggregated distribution values, associated with a * histogram bucket. * * @since 0.17 */ @Immutable @AutoValue public abstract static class Exemplar { Exemplar() {} /** * Returns value of the {@link Exemplar} point. * * @return value of the {@code Exemplar} point. * @since 0.17 */ 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.17 */ 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.17 */ public abstract Map 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.17 */ public static Exemplar create( double value, Timestamp timestamp, Map attachments) { Utils.checkNotNull(attachments, "attachments"); Map attachmentsCopy = Collections.unmodifiableMap(new HashMap(attachments)); for (Entry 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); } } }