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.common.Timestamp; 23 import io.opencensus.internal.Utils; 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.Map.Entry; 30 import javax.annotation.Nullable; 31 import javax.annotation.concurrent.Immutable; 32 33 /** 34 * {@link Distribution} contains summary statistics for a population of values. It optionally 35 * contains a histogram representing the distribution of those values across a set of buckets. 36 * 37 * @since 0.17 38 */ 39 @ExperimentalApi 40 @AutoValue 41 @Immutable 42 public abstract class Distribution { 43 44 Distribution() {} 45 46 /** 47 * Creates a {@link Distribution}. 48 * 49 * @param count the count of the population values. 50 * @param sum the sum of the population values. 51 * @param sumOfSquaredDeviations the sum of squared deviations of the population values. 52 * @param bucketOptions the bucket options used to create a histogram for the distribution. 53 * @param buckets {@link Bucket}s of a histogram. 54 * @return a {@code Distribution}. 55 * @since 0.17 56 */ 57 public static Distribution create( 58 long count, 59 double sum, 60 double sumOfSquaredDeviations, 61 BucketOptions bucketOptions, 62 List<Bucket> buckets) { 63 Utils.checkArgument(count >= 0, "count should be non-negative."); 64 Utils.checkArgument( 65 sumOfSquaredDeviations >= 0, "sum of squared deviations should be non-negative."); 66 if (count == 0) { 67 Utils.checkArgument(sum == 0, "sum should be 0 if count is 0."); 68 Utils.checkArgument( 69 sumOfSquaredDeviations == 0, "sum of squared deviations should be 0 if count is 0."); 70 } 71 Utils.checkNotNull(bucketOptions, "bucketOptions"); 72 List<Bucket> bucketsCopy = 73 Collections.unmodifiableList(new ArrayList<Bucket>(Utils.checkNotNull(buckets, "buckets"))); 74 Utils.checkListElementNotNull(bucketsCopy, "bucket"); 75 return new AutoValue_Distribution( 76 count, sum, sumOfSquaredDeviations, bucketOptions, bucketsCopy); 77 } 78 79 /** 80 * Returns the aggregated count. 81 * 82 * @return the aggregated count. 83 * @since 0.17 84 */ 85 public abstract long getCount(); 86 87 /** 88 * Returns the aggregated sum. 89 * 90 * @return the aggregated sum. 91 * @since 0.17 92 */ 93 public abstract double getSum(); 94 95 /** 96 * Returns the aggregated sum of squared deviations. 97 * 98 * <p>The sum of squared deviations from the mean of the values in the population. For values x_i 99 * this is: 100 * 101 * <p>Sum[i=1..n]((x_i - mean)^2) 102 * 103 * <p>If count is zero then this field must be zero. 104 * 105 * @return the aggregated sum of squared deviations. 106 * @since 0.17 107 */ 108 public abstract double getSumOfSquaredDeviations(); 109 110 /** 111 * Returns bucket options used to create a histogram for the distribution. 112 * 113 * @return the {@code BucketOptions} associated with the {@code Distribution}, or {@code null} if 114 * there isn't one. 115 * @since 0.17 116 */ 117 @Nullable 118 public abstract BucketOptions getBucketOptions(); 119 120 /** 121 * Returns the aggregated histogram {@link Bucket}s. 122 * 123 * @return the aggregated histogram buckets. 124 * @since 0.17 125 */ 126 public abstract List<Bucket> getBuckets(); 127 128 /** 129 * The bucket options used to create a histogram for the distribution. 130 * 131 * @since 0.17 132 */ 133 @Immutable 134 public abstract static class BucketOptions { 135 136 private BucketOptions() {} 137 138 /** 139 * Returns a {@link ExplicitOptions}. 140 * 141 * <p>The bucket boundaries for that histogram are described by bucket_bounds. This defines 142 * size(bucket_bounds) + 1 (= N) buckets. The boundaries for bucket index i are: 143 * 144 * <ul> 145 * <li>{@code [0, bucket_bounds[i]) for i == 0} 146 * <li>{@code [bucket_bounds[i-1], bucket_bounds[i]) for 0 < i < N-1} 147 * <li>{@code [bucket_bounds[i-1], +infinity) for i == N-1} 148 * </ul> 149 * 150 * <p>If bucket_bounds has no elements (zero size), then there is no histogram associated with 151 * the Distribution. If bucket_bounds has only one element, there are no finite buckets, and 152 * that single element is the common boundary of the overflow and underflow buckets. The values 153 * must be monotonically increasing. 154 * 155 * @param bucketBoundaries the bucket boundaries of a distribution (given explicitly). The 156 * values must be strictly increasing and should be positive values. 157 * @return a {@code ExplicitOptions} {@code BucketOptions}. 158 * @since 0.17 159 */ 160 public static BucketOptions explicitOptions(List<Double> bucketBoundaries) { 161 return ExplicitOptions.create(bucketBoundaries); 162 } 163 164 /** 165 * Applies the given match function to the underlying BucketOptions. 166 * 167 * @param explicitFunction the function that should be applied if the BucketOptions has type 168 * {@code ExplicitOptions}. 169 * @param defaultFunction the function that should be applied if the BucketOptions has a type 170 * that was added after this {@code match} method was added to the API. See {@link 171 * io.opencensus.common.Functions} for some common functions for handling unknown types. 172 * @return the result of the function applied to the underlying BucketOptions. 173 * @since 0.17 174 */ 175 public abstract <T> T match( 176 Function<? super ExplicitOptions, T> explicitFunction, 177 Function<? super BucketOptions, T> defaultFunction); 178 179 /** A Bucket with explicit bounds {@link BucketOptions}. */ 180 @AutoValue 181 @Immutable 182 public abstract static class ExplicitOptions extends BucketOptions { 183 184 ExplicitOptions() {} 185 186 @Override 187 public final <T> T match( 188 Function<? super ExplicitOptions, T> explicitFunction, 189 Function<? super BucketOptions, T> defaultFunction) { 190 return explicitFunction.apply(this); 191 } 192 193 /** 194 * Creates a {@link ExplicitOptions}. 195 * 196 * @param bucketBoundaries the bucket boundaries of a distribution (given explicitly). The 197 * values must be strictly increasing and should be positive. 198 * @return a {@code ExplicitOptions}. 199 * @since 0.17 200 */ 201 private static ExplicitOptions create(List<Double> bucketBoundaries) { 202 Utils.checkNotNull(bucketBoundaries, "bucketBoundaries"); 203 List<Double> bucketBoundariesCopy = 204 Collections.unmodifiableList(new ArrayList<Double>(bucketBoundaries)); 205 checkBucketBoundsAreSorted(bucketBoundariesCopy); 206 return new AutoValue_Distribution_BucketOptions_ExplicitOptions(bucketBoundariesCopy); 207 } 208 209 private static void checkBucketBoundsAreSorted(List<Double> bucketBoundaries) { 210 if (bucketBoundaries.size() >= 1) { 211 double previous = Utils.checkNotNull(bucketBoundaries.get(0), "bucketBoundary"); 212 Utils.checkArgument(previous > 0, "bucket boundary should be > 0"); 213 for (int i = 1; i < bucketBoundaries.size(); i++) { 214 double next = Utils.checkNotNull(bucketBoundaries.get(i), "bucketBoundary"); 215 Utils.checkArgument(previous < next, "bucket boundaries not sorted."); 216 previous = next; 217 } 218 } 219 } 220 221 /** 222 * Returns the bucket boundaries of this distribution. 223 * 224 * @return the bucket boundaries of this distribution. 225 * @since 0.17 226 */ 227 public abstract List<Double> getBucketBoundaries(); 228 } 229 } 230 231 /** 232 * The histogram bucket of the population values. 233 * 234 * @since 0.17 235 */ 236 @AutoValue 237 @Immutable 238 public abstract static class Bucket { 239 240 Bucket() {} 241 242 /** 243 * Creates a {@link Bucket}. 244 * 245 * @param count the number of values in each bucket of the histogram. 246 * @return a {@code Bucket}. 247 * @since 0.17 248 */ 249 public static Bucket create(long count) { 250 Utils.checkArgument(count >= 0, "bucket count should be non-negative."); 251 return new AutoValue_Distribution_Bucket(count, null); 252 } 253 254 /** 255 * Creates a {@link Bucket} with an {@link Exemplar}. 256 * 257 * @param count the number of values in each bucket of the histogram. 258 * @param exemplar the {@code Exemplar} of this {@code Bucket}. 259 * @return a {@code Bucket}. 260 * @since 0.17 261 */ 262 public static Bucket create(long count, Exemplar exemplar) { 263 Utils.checkArgument(count >= 0, "bucket count should be non-negative."); 264 Utils.checkNotNull(exemplar, "exemplar"); 265 return new AutoValue_Distribution_Bucket(count, exemplar); 266 } 267 268 /** 269 * Returns the number of values in each bucket of the histogram. 270 * 271 * @return the number of values in each bucket of the histogram. 272 * @since 0.17 273 */ 274 public abstract long getCount(); 275 276 /** 277 * Returns the {@link Exemplar} associated with the {@link Bucket}, or {@code null} if there 278 * isn't one. 279 * 280 * @return the {@code Exemplar} associated with the {@code Bucket}, or {@code null} if there 281 * isn't one. 282 * @since 0.17 283 */ 284 @Nullable 285 public abstract Exemplar getExemplar(); 286 } 287 288 /** 289 * An example point that may be used to annotate aggregated distribution values, associated with a 290 * histogram bucket. 291 * 292 * @since 0.17 293 */ 294 @Immutable 295 @AutoValue 296 public abstract static class Exemplar { 297 298 Exemplar() {} 299 300 /** 301 * Returns value of the {@link Exemplar} point. 302 * 303 * @return value of the {@code Exemplar} point. 304 * @since 0.17 305 */ 306 public abstract double getValue(); 307 308 /** 309 * Returns the time that this {@link Exemplar}'s value was recorded. 310 * 311 * @return the time that this {@code Exemplar}'s value was recorded. 312 * @since 0.17 313 */ 314 public abstract Timestamp getTimestamp(); 315 316 /** 317 * Returns the contextual information about the example value, represented as a string map. 318 * 319 * @return the contextual information about the example value. 320 * @since 0.17 321 */ 322 public abstract Map<String, String> getAttachments(); 323 324 /** 325 * Creates an {@link Exemplar}. 326 * 327 * @param value value of the {@link Exemplar} point. 328 * @param timestamp the time that this {@code Exemplar}'s value was recorded. 329 * @param attachments the contextual information about the example value. 330 * @return an {@code Exemplar}. 331 * @since 0.17 332 */ 333 public static Exemplar create( 334 double value, Timestamp timestamp, Map<String, String> attachments) { 335 Utils.checkNotNull(attachments, "attachments"); 336 Map<String, String> attachmentsCopy = 337 Collections.unmodifiableMap(new HashMap<String, String>(attachments)); 338 for (Entry<String, String> entry : attachmentsCopy.entrySet()) { 339 Utils.checkNotNull(entry.getKey(), "key of attachments"); 340 Utils.checkNotNull(entry.getValue(), "value of attachments"); 341 } 342 return new AutoValue_Distribution_Exemplar(value, timestamp, attachmentsCopy); 343 } 344 } 345 } 346