1 /* 2 * Copyright 2018, OpenCensus Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.opencensus.metrics.export; 18 19 import com.google.auto.value.AutoValue; 20 import io.opencensus.common.ExperimentalApi; 21 import io.opencensus.common.Function; 22 import io.opencensus.internal.Utils; 23 import io.opencensus.metrics.data.Exemplar; 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.List; 27 import javax.annotation.Nullable; 28 import javax.annotation.concurrent.Immutable; 29 30 /** 31 * {@link Distribution} contains summary statistics for a population of values. It optionally 32 * contains a histogram representing the distribution of those values across a set of buckets. 33 * 34 * @since 0.17 35 */ 36 @ExperimentalApi 37 @AutoValue 38 @Immutable 39 public abstract class Distribution { 40 Distribution()41 Distribution() {} 42 43 /** 44 * Creates a {@link Distribution}. 45 * 46 * @param count the count of the population values. 47 * @param sum the sum of the population values. 48 * @param sumOfSquaredDeviations the sum of squared deviations of the population values. 49 * @param bucketOptions the bucket options used to create a histogram for the distribution. 50 * @param buckets {@link Bucket}s of a histogram. 51 * @return a {@code Distribution}. 52 * @since 0.17 53 */ create( long count, double sum, double sumOfSquaredDeviations, BucketOptions bucketOptions, List<Bucket> buckets)54 public static Distribution create( 55 long count, 56 double sum, 57 double sumOfSquaredDeviations, 58 BucketOptions bucketOptions, 59 List<Bucket> buckets) { 60 Utils.checkArgument(count >= 0, "count should be non-negative."); 61 Utils.checkArgument( 62 sumOfSquaredDeviations >= 0, "sum of squared deviations should be non-negative."); 63 if (count == 0) { 64 Utils.checkArgument(sum == 0, "sum should be 0 if count is 0."); 65 Utils.checkArgument( 66 sumOfSquaredDeviations == 0, "sum of squared deviations should be 0 if count is 0."); 67 } 68 Utils.checkNotNull(bucketOptions, "bucketOptions"); 69 List<Bucket> bucketsCopy = 70 Collections.unmodifiableList(new ArrayList<Bucket>(Utils.checkNotNull(buckets, "buckets"))); 71 Utils.checkListElementNotNull(bucketsCopy, "bucket"); 72 return new AutoValue_Distribution( 73 count, sum, sumOfSquaredDeviations, bucketOptions, bucketsCopy); 74 } 75 76 /** 77 * Returns the aggregated count. 78 * 79 * @return the aggregated count. 80 * @since 0.17 81 */ getCount()82 public abstract long getCount(); 83 84 /** 85 * Returns the aggregated sum. 86 * 87 * @return the aggregated sum. 88 * @since 0.17 89 */ getSum()90 public abstract double getSum(); 91 92 /** 93 * Returns the aggregated sum of squared deviations. 94 * 95 * <p>The sum of squared deviations from the mean of the values in the population. For values x_i 96 * this is: 97 * 98 * <p>Sum[i=1..n]((x_i - mean)^2) 99 * 100 * <p>If count is zero then this field must be zero. 101 * 102 * @return the aggregated sum of squared deviations. 103 * @since 0.17 104 */ getSumOfSquaredDeviations()105 public abstract double getSumOfSquaredDeviations(); 106 107 /** 108 * Returns bucket options used to create a histogram for the distribution. 109 * 110 * @return the {@code BucketOptions} associated with the {@code Distribution}, or {@code null} if 111 * there isn't one. 112 * @since 0.17 113 */ 114 @Nullable getBucketOptions()115 public abstract BucketOptions getBucketOptions(); 116 117 /** 118 * Returns the aggregated histogram {@link Bucket}s. 119 * 120 * @return the aggregated histogram buckets. 121 * @since 0.17 122 */ getBuckets()123 public abstract List<Bucket> getBuckets(); 124 125 /** 126 * The bucket options used to create a histogram for the distribution. 127 * 128 * @since 0.17 129 */ 130 @Immutable 131 public abstract static class BucketOptions { 132 BucketOptions()133 private BucketOptions() {} 134 135 /** 136 * Returns a {@link ExplicitOptions}. 137 * 138 * <p>The bucket boundaries for that histogram are described by bucket_bounds. This defines 139 * size(bucket_bounds) + 1 (= N) buckets. The boundaries for bucket index i are: 140 * 141 * <ul> 142 * <li>{@code [0, bucket_bounds[i]) for i == 0} 143 * <li>{@code [bucket_bounds[i-1], bucket_bounds[i]) for 0 < i < N-1} 144 * <li>{@code [bucket_bounds[i-1], +infinity) for i == N-1} 145 * </ul> 146 * 147 * <p>If bucket_bounds has no elements (zero size), then there is no histogram associated with 148 * the Distribution. If bucket_bounds has only one element, there are no finite buckets, and 149 * that single element is the common boundary of the overflow and underflow buckets. The values 150 * must be monotonically increasing. 151 * 152 * @param bucketBoundaries the bucket boundaries of a distribution (given explicitly). The 153 * values must be strictly increasing and should be positive values. 154 * @return a {@code ExplicitOptions} {@code BucketOptions}. 155 * @since 0.17 156 */ explicitOptions(List<Double> bucketBoundaries)157 public static BucketOptions explicitOptions(List<Double> bucketBoundaries) { 158 return ExplicitOptions.create(bucketBoundaries); 159 } 160 161 /** 162 * Applies the given match function to the underlying BucketOptions. 163 * 164 * @param explicitFunction the function that should be applied if the BucketOptions has type 165 * {@code ExplicitOptions}. 166 * @param defaultFunction the function that should be applied if the BucketOptions has a type 167 * that was added after this {@code match} method was added to the API. See {@link 168 * io.opencensus.common.Functions} for some common functions for handling unknown types. 169 * @return the result of the function applied to the underlying BucketOptions. 170 * @since 0.17 171 */ match( Function<? super ExplicitOptions, T> explicitFunction, Function<? super BucketOptions, T> defaultFunction)172 public abstract <T> T match( 173 Function<? super ExplicitOptions, T> explicitFunction, 174 Function<? super BucketOptions, T> defaultFunction); 175 176 /** A Bucket with explicit bounds {@link BucketOptions}. */ 177 @AutoValue 178 @Immutable 179 public abstract static class ExplicitOptions extends BucketOptions { 180 ExplicitOptions()181 ExplicitOptions() {} 182 183 @Override match( Function<? super ExplicitOptions, T> explicitFunction, Function<? super BucketOptions, T> defaultFunction)184 public final <T> T match( 185 Function<? super ExplicitOptions, T> explicitFunction, 186 Function<? super BucketOptions, T> defaultFunction) { 187 return explicitFunction.apply(this); 188 } 189 190 /** 191 * Creates a {@link ExplicitOptions}. 192 * 193 * @param bucketBoundaries the bucket boundaries of a distribution (given explicitly). The 194 * values must be strictly increasing and should be positive. 195 * @return a {@code ExplicitOptions}. 196 * @since 0.17 197 */ create(List<Double> bucketBoundaries)198 private static ExplicitOptions create(List<Double> bucketBoundaries) { 199 Utils.checkNotNull(bucketBoundaries, "bucketBoundaries"); 200 List<Double> bucketBoundariesCopy = 201 Collections.unmodifiableList(new ArrayList<Double>(bucketBoundaries)); 202 checkBucketBoundsAreSorted(bucketBoundariesCopy); 203 return new AutoValue_Distribution_BucketOptions_ExplicitOptions(bucketBoundariesCopy); 204 } 205 checkBucketBoundsAreSorted(List<Double> bucketBoundaries)206 private static void checkBucketBoundsAreSorted(List<Double> bucketBoundaries) { 207 if (bucketBoundaries.size() >= 1) { 208 double previous = Utils.checkNotNull(bucketBoundaries.get(0), "bucketBoundary"); 209 Utils.checkArgument(previous > 0, "bucket boundary should be > 0"); 210 for (int i = 1; i < bucketBoundaries.size(); i++) { 211 double next = Utils.checkNotNull(bucketBoundaries.get(i), "bucketBoundary"); 212 Utils.checkArgument(previous < next, "bucket boundaries not sorted."); 213 previous = next; 214 } 215 } 216 } 217 218 /** 219 * Returns the bucket boundaries of this distribution. 220 * 221 * @return the bucket boundaries of this distribution. 222 * @since 0.17 223 */ 224 public abstract List<Double> getBucketBoundaries(); 225 } 226 } 227 228 /** 229 * The histogram bucket of the population values. 230 * 231 * @since 0.17 232 */ 233 @AutoValue 234 @Immutable 235 public abstract static class Bucket { 236 237 Bucket() {} 238 239 /** 240 * Creates a {@link Bucket}. 241 * 242 * @param count the number of values in each bucket of the histogram. 243 * @return a {@code Bucket}. 244 * @since 0.17 245 */ 246 public static Bucket create(long count) { 247 Utils.checkArgument(count >= 0, "bucket count should be non-negative."); 248 return new AutoValue_Distribution_Bucket(count, null); 249 } 250 251 /** 252 * Creates a {@link Bucket} with an {@link Exemplar}. 253 * 254 * @param count the number of values in each bucket of the histogram. 255 * @param exemplar the {@code Exemplar} of this {@code Bucket}. 256 * @return a {@code Bucket}. 257 * @since 0.17 258 */ create(long count, Exemplar exemplar)259 public static Bucket create(long count, Exemplar exemplar) { 260 Utils.checkArgument(count >= 0, "bucket count should be non-negative."); 261 Utils.checkNotNull(exemplar, "exemplar"); 262 return new AutoValue_Distribution_Bucket(count, exemplar); 263 } 264 265 /** 266 * Returns the number of values in each bucket of the histogram. 267 * 268 * @return the number of values in each bucket of the histogram. 269 * @since 0.17 270 */ 271 public abstract long getCount(); 272 273 /** 274 * Returns the {@link Exemplar} associated with the {@link Bucket}, or {@code null} if there 275 * isn't one. 276 * 277 * @return the {@code Exemplar} associated with the {@code Bucket}, or {@code null} if there 278 * isn't one. 279 * @since 0.17 280 */ 281 @Nullable 282 public abstract Exemplar getExemplar(); 283 } 284 } 285