1 /* 2 * Copyright 2017, 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.trace.export; 18 19 import com.google.auto.value.AutoValue; 20 import io.opencensus.internal.Utils; 21 import io.opencensus.trace.Span; 22 import io.opencensus.trace.Status; 23 import io.opencensus.trace.Status.CanonicalCode; 24 import java.util.Collection; 25 import java.util.Collections; 26 import java.util.HashMap; 27 import java.util.HashSet; 28 import java.util.Map; 29 import java.util.Set; 30 import java.util.concurrent.TimeUnit; 31 import javax.annotation.Nullable; 32 import javax.annotation.concurrent.GuardedBy; 33 import javax.annotation.concurrent.Immutable; 34 import javax.annotation.concurrent.ThreadSafe; 35 36 /** 37 * This class allows users to access in-process information such as latency based sampled spans and 38 * error based sampled spans. 39 * 40 * <p>For all completed spans with the option {@link Span.Options#RECORD_EVENTS} the library can 41 * store samples based on latency for succeeded operations or based on error code for failed 42 * operations. To activate this, users MUST manually configure all the span names for which samples 43 * will be collected (see {@link #registerSpanNamesForCollection(Collection)}). 44 * 45 * @since 0.5 46 */ 47 @ThreadSafe 48 public abstract class SampledSpanStore { 49 50 protected SampledSpanStore() {} 51 52 /** 53 * Returns a {@code SampledSpanStore} that maintains a set of span names, but always returns an 54 * empty list of {@link SpanData}. 55 * 56 * @return a {@code SampledSpanStore} that maintains a set of span names, but always returns an 57 * empty list of {@code SpanData}. 58 */ 59 static SampledSpanStore newNoopSampledSpanStore() { 60 return new NoopSampledSpanStore(); 61 } 62 63 /** 64 * Returns the summary of all available data, such as number of sampled spans in the latency based 65 * samples or error based samples. 66 * 67 * <p>Data available only for span names registered using {@link 68 * #registerSpanNamesForCollection(Collection)}. 69 * 70 * @return the summary of all available data. 71 * @since 0.5 72 */ 73 public abstract Summary getSummary(); 74 75 /** 76 * Returns a list of succeeded spans (spans with {@link Status} equal to {@link Status#OK}) that 77 * match the {@code filter}. 78 * 79 * <p>Latency based sampled spans are available only for span names registered using {@link 80 * #registerSpanNamesForCollection(Collection)}. 81 * 82 * @param filter used to filter the returned sampled spans. 83 * @return a list of succeeded spans that match the {@code filter}. 84 * @since 0.5 85 */ 86 public abstract Collection<SpanData> getLatencySampledSpans(LatencyFilter filter); 87 88 /** 89 * Returns a list of failed spans (spans with {@link Status} other than {@link Status#OK}) that 90 * match the {@code filter}. 91 * 92 * <p>Error based sampled spans are available only for span names registered using {@link 93 * #registerSpanNamesForCollection(Collection)}. 94 * 95 * @param filter used to filter the returned sampled spans. 96 * @return a list of failed spans that match the {@code filter}. 97 * @since 0.5 98 */ 99 public abstract Collection<SpanData> getErrorSampledSpans(ErrorFilter filter); 100 101 /** 102 * Appends a list of span names for which the library will collect latency based sampled spans and 103 * error based sampled spans. 104 * 105 * <p>If called multiple times the library keeps the list of unique span names from all the calls. 106 * 107 * @param spanNames list of span names for which the library will collect samples. 108 * @since 0.5 109 */ 110 public abstract void registerSpanNamesForCollection(Collection<String> spanNames); 111 112 /** 113 * Removes a list of span names for which the library will collect latency based sampled spans and 114 * error based sampled spans. 115 * 116 * <p>The library keeps the list of unique registered span names for which samples will be called. 117 * This method allows users to remove span names from that list. 118 * 119 * @param spanNames list of span names for which the library will no longer collect samples. 120 * @since 0.5 121 */ 122 public abstract void unregisterSpanNamesForCollection(Collection<String> spanNames); 123 124 /** 125 * Returns the set of unique span names registered to the library, for use in tests. For this set 126 * of span names the library will collect latency based sampled spans and error based sampled 127 * spans. 128 * 129 * <p>This method is only meant for testing code that uses OpenCensus, and it is not performant. 130 * 131 * @return the set of unique span names registered to the library. 132 * @since 0.7 133 */ 134 public abstract Set<String> getRegisteredSpanNamesForCollection(); 135 136 /** 137 * The summary of all available data. 138 * 139 * @since 0.5 140 */ 141 @AutoValue 142 @Immutable 143 public abstract static class Summary { 144 145 Summary() {} 146 147 /** 148 * Returns a new instance of {@code Summary}. 149 * 150 * @param perSpanNameSummary a map with summary for each span name. 151 * @return a new instance of {@code Summary}. 152 * @throws NullPointerException if {@code perSpanNameSummary} is {@code null}. 153 * @since 0.5 154 */ 155 public static Summary create(Map<String, PerSpanNameSummary> perSpanNameSummary) { 156 return new AutoValue_SampledSpanStore_Summary( 157 Collections.unmodifiableMap( 158 new HashMap<String, PerSpanNameSummary>( 159 Utils.checkNotNull(perSpanNameSummary, "perSpanNameSummary")))); 160 } 161 162 /** 163 * Returns a map with summary of available data for each span name. 164 * 165 * @return a map with all the span names and the summary. 166 * @since 0.5 167 */ 168 public abstract Map<String, PerSpanNameSummary> getPerSpanNameSummary(); 169 } 170 171 /** 172 * Summary of all available data for a span name. 173 * 174 * @since 0.5 175 */ 176 @AutoValue 177 @Immutable 178 public abstract static class PerSpanNameSummary { 179 180 PerSpanNameSummary() {} 181 182 /** 183 * Returns a new instance of {@code PerSpanNameSummary}. 184 * 185 * @param numbersOfLatencySampledSpans the summary for the latency buckets. 186 * @param numbersOfErrorSampledSpans the summary for the error buckets. 187 * @return a new instance of {@code PerSpanNameSummary}. 188 * @throws NullPointerException if {@code numbersOfLatencySampledSpans} or {@code 189 * numbersOfErrorSampledSpans} are {@code null}. 190 * @since 0.5 191 */ 192 public static PerSpanNameSummary create( 193 Map<LatencyBucketBoundaries, Integer> numbersOfLatencySampledSpans, 194 Map<CanonicalCode, Integer> numbersOfErrorSampledSpans) { 195 return new AutoValue_SampledSpanStore_PerSpanNameSummary( 196 Collections.unmodifiableMap( 197 new HashMap<LatencyBucketBoundaries, Integer>( 198 Utils.checkNotNull( 199 numbersOfLatencySampledSpans, "numbersOfLatencySampledSpans"))), 200 Collections.unmodifiableMap( 201 new HashMap<CanonicalCode, Integer>( 202 Utils.checkNotNull(numbersOfErrorSampledSpans, "numbersOfErrorSampledSpans")))); 203 } 204 205 /** 206 * Returns the number of sampled spans in all the latency buckets. 207 * 208 * <p>Data available only for span names registered using {@link 209 * #registerSpanNamesForCollection(Collection)}. 210 * 211 * @return the number of sampled spans in all the latency buckets. 212 * @since 0.5 213 */ 214 public abstract Map<LatencyBucketBoundaries, Integer> getNumbersOfLatencySampledSpans(); 215 216 /** 217 * Returns the number of sampled spans in all the error buckets. 218 * 219 * <p>Data available only for span names registered using {@link 220 * #registerSpanNamesForCollection(Collection)}. 221 * 222 * @return the number of sampled spans in all the error buckets. 223 * @since 0.5 224 */ 225 public abstract Map<CanonicalCode, Integer> getNumbersOfErrorSampledSpans(); 226 } 227 228 /** 229 * The latency buckets boundaries. Samples based on latency for successful spans (the status of 230 * the span has a canonical code equal to {@link CanonicalCode#OK}) are collected in one of these 231 * latency buckets. 232 * 233 * @since 0.5 234 */ 235 public enum LatencyBucketBoundaries { 236 /** 237 * Stores finished successful requests of duration within the interval [0, 10us). 238 * 239 * @since 0.5 240 */ 241 ZERO_MICROSx10(0, TimeUnit.MICROSECONDS.toNanos(10)), 242 243 /** 244 * Stores finished successful requests of duration within the interval [10us, 100us). 245 * 246 * @since 0.5 247 */ 248 MICROSx10_MICROSx100(TimeUnit.MICROSECONDS.toNanos(10), TimeUnit.MICROSECONDS.toNanos(100)), 249 250 /** 251 * Stores finished successful requests of duration within the interval [100us, 1ms). 252 * 253 * @since 0.5 254 */ 255 MICROSx100_MILLIx1(TimeUnit.MICROSECONDS.toNanos(100), TimeUnit.MILLISECONDS.toNanos(1)), 256 257 /** 258 * Stores finished successful requests of duration within the interval [1ms, 10ms). 259 * 260 * @since 0.5 261 */ 262 MILLIx1_MILLIx10(TimeUnit.MILLISECONDS.toNanos(1), TimeUnit.MILLISECONDS.toNanos(10)), 263 264 /** 265 * Stores finished successful requests of duration within the interval [10ms, 100ms). 266 * 267 * @since 0.5 268 */ 269 MILLIx10_MILLIx100(TimeUnit.MILLISECONDS.toNanos(10), TimeUnit.MILLISECONDS.toNanos(100)), 270 271 /** 272 * Stores finished successful requests of duration within the interval [100ms, 1sec). 273 * 274 * @since 0.5 275 */ 276 MILLIx100_SECONDx1(TimeUnit.MILLISECONDS.toNanos(100), TimeUnit.SECONDS.toNanos(1)), 277 278 /** 279 * Stores finished successful requests of duration within the interval [1sec, 10sec). 280 * 281 * @since 0.5 282 */ 283 SECONDx1_SECONDx10(TimeUnit.SECONDS.toNanos(1), TimeUnit.SECONDS.toNanos(10)), 284 285 /** 286 * Stores finished successful requests of duration within the interval [10sec, 100sec). 287 * 288 * @since 0.5 289 */ 290 SECONDx10_SECONDx100(TimeUnit.SECONDS.toNanos(10), TimeUnit.SECONDS.toNanos(100)), 291 292 /** 293 * Stores finished successful requests of duration >= 100sec. 294 * 295 * @since 0.5 296 */ 297 SECONDx100_MAX(TimeUnit.SECONDS.toNanos(100), Long.MAX_VALUE); 298 299 /** 300 * Constructs a {@code LatencyBucketBoundaries} with the given boundaries and label. 301 * 302 * @param latencyLowerNs the latency lower bound of the bucket. 303 * @param latencyUpperNs the latency upper bound of the bucket. 304 */ 305 LatencyBucketBoundaries(long latencyLowerNs, long latencyUpperNs) { 306 this.latencyLowerNs = latencyLowerNs; 307 this.latencyUpperNs = latencyUpperNs; 308 } 309 310 /** 311 * Returns the latency lower bound of the bucket. 312 * 313 * @return the latency lower bound of the bucket. 314 * @since 0.5 315 */ 316 public long getLatencyLowerNs() { 317 return latencyLowerNs; 318 } 319 320 /** 321 * Returns the latency upper bound of the bucket. 322 * 323 * @return the latency upper bound of the bucket. 324 * @since 0.5 325 */ 326 public long getLatencyUpperNs() { 327 return latencyUpperNs; 328 } 329 330 private final long latencyLowerNs; 331 private final long latencyUpperNs; 332 } 333 334 /** 335 * Filter for latency based sampled spans. Used to filter results returned by the {@link 336 * #getLatencySampledSpans(LatencyFilter)} request. 337 * 338 * @since 0.5 339 */ 340 @AutoValue 341 @Immutable 342 public abstract static class LatencyFilter { 343 344 LatencyFilter() {} 345 346 /** 347 * Returns a new instance of {@code LatencyFilter}. 348 * 349 * <p>Filters all the spans based on {@code spanName} and latency in the interval 350 * [latencyLowerNs, latencyUpperNs) and returns a maximum of {@code maxSpansToReturn}. 351 * 352 * @param spanName the name of the span. 353 * @param latencyLowerNs the latency lower bound. 354 * @param latencyUpperNs the latency upper bound. 355 * @param maxSpansToReturn the maximum number of results to be returned. {@code 0} means all. 356 * @return a new instance of {@code LatencyFilter}. 357 * @throws NullPointerException if {@code spanName} is {@code null}. 358 * @throws IllegalArgumentException if {@code maxSpansToReturn} or {@code latencyLowerNs} or 359 * {@code latencyUpperNs} are negative. 360 * @since 0.5 361 */ 362 public static LatencyFilter create( 363 String spanName, long latencyLowerNs, long latencyUpperNs, int maxSpansToReturn) { 364 Utils.checkArgument(maxSpansToReturn >= 0, "Negative maxSpansToReturn."); 365 Utils.checkArgument(latencyLowerNs >= 0, "Negative latencyLowerNs"); 366 Utils.checkArgument(latencyUpperNs >= 0, "Negative latencyUpperNs"); 367 return new AutoValue_SampledSpanStore_LatencyFilter( 368 spanName, latencyLowerNs, latencyUpperNs, maxSpansToReturn); 369 } 370 371 /** 372 * Returns the span name used by this filter. 373 * 374 * @return the span name used by this filter. 375 * @since 0.5 376 */ 377 public abstract String getSpanName(); 378 379 /** 380 * Returns the latency lower bound of this bucket (inclusive). 381 * 382 * @return the latency lower bound of this bucket. 383 * @since 0.5 384 */ 385 public abstract long getLatencyLowerNs(); 386 387 /** 388 * Returns the latency upper bound of this bucket (exclusive). 389 * 390 * @return the latency upper bound of this bucket. 391 * @since 0.5 392 */ 393 public abstract long getLatencyUpperNs(); 394 395 /** 396 * Returns the maximum number of spans to be returned. {@code 0} means all. 397 * 398 * @return the maximum number of spans to be returned. 399 * @since 0.5 400 */ 401 public abstract int getMaxSpansToReturn(); 402 } 403 404 /** 405 * Filter for error based sampled spans. Used to filter results returned by the {@link 406 * #getErrorSampledSpans(ErrorFilter)} request. 407 * 408 * @since 0.5 409 */ 410 @AutoValue 411 @Immutable 412 public abstract static class ErrorFilter { 413 414 ErrorFilter() {} 415 416 /** 417 * Returns a new instance of {@code ErrorFilter}. 418 * 419 * <p>Filters all the spans based on {@code spanName} and {@code canonicalCode} and returns a 420 * maximum of {@code maxSpansToReturn}. 421 * 422 * @param spanName the name of the span. 423 * @param canonicalCode the error code of the span. {@code null} can be used to query all error 424 * codes. 425 * @param maxSpansToReturn the maximum number of results to be returned. {@code 0} means all. 426 * @return a new instance of {@code ErrorFilter}. 427 * @throws NullPointerException if {@code spanName} is {@code null}. 428 * @throws IllegalArgumentException if {@code canonicalCode} is {@link CanonicalCode#OK} or 429 * {@code maxSpansToReturn} is negative. 430 * @since 0.5 431 */ 432 public static ErrorFilter create( 433 String spanName, @Nullable CanonicalCode canonicalCode, int maxSpansToReturn) { 434 if (canonicalCode != null) { 435 Utils.checkArgument(canonicalCode != CanonicalCode.OK, "Invalid canonical code."); 436 } 437 Utils.checkArgument(maxSpansToReturn >= 0, "Negative maxSpansToReturn."); 438 return new AutoValue_SampledSpanStore_ErrorFilter(spanName, canonicalCode, maxSpansToReturn); 439 } 440 441 /** 442 * Returns the span name used by this filter. 443 * 444 * @return the span name used by this filter. 445 * @since 0.5 446 */ 447 public abstract String getSpanName(); 448 449 /** 450 * Returns the canonical code used by this filter. Always different than {@link 451 * CanonicalCode#OK}. If {@code null} then all errors match. 452 * 453 * @return the canonical code used by this filter. 454 * @since 0.5 455 */ 456 @Nullable 457 public abstract CanonicalCode getCanonicalCode(); 458 459 /** 460 * Returns the maximum number of spans to be returned. Used to enforce the number of returned 461 * {@code SpanData}. {@code 0} means all. 462 * 463 * @return the maximum number of spans to be returned. 464 * @since 0.5 465 */ 466 public abstract int getMaxSpansToReturn(); 467 } 468 469 @ThreadSafe 470 private static final class NoopSampledSpanStore extends SampledSpanStore { 471 private static final PerSpanNameSummary EMPTY_PER_SPAN_NAME_SUMMARY = 472 PerSpanNameSummary.create( 473 Collections.<SampledSpanStore.LatencyBucketBoundaries, Integer>emptyMap(), 474 Collections.<CanonicalCode, Integer>emptyMap()); 475 476 @GuardedBy("registeredSpanNames") 477 private final Set<String> registeredSpanNames = new HashSet<String>(); 478 479 @Override 480 public Summary getSummary() { 481 Map<String, PerSpanNameSummary> result = new HashMap<String, PerSpanNameSummary>(); 482 synchronized (registeredSpanNames) { 483 for (String registeredSpanName : registeredSpanNames) { 484 result.put(registeredSpanName, EMPTY_PER_SPAN_NAME_SUMMARY); 485 } 486 } 487 return Summary.create(result); 488 } 489 490 @Override 491 public Collection<SpanData> getLatencySampledSpans(LatencyFilter filter) { 492 Utils.checkNotNull(filter, "latencyFilter"); 493 return Collections.<SpanData>emptyList(); 494 } 495 496 @Override 497 public Collection<SpanData> getErrorSampledSpans(ErrorFilter filter) { 498 Utils.checkNotNull(filter, "errorFilter"); 499 return Collections.<SpanData>emptyList(); 500 } 501 502 @Override 503 public void registerSpanNamesForCollection(Collection<String> spanNames) { 504 Utils.checkNotNull(spanNames, "spanNames"); 505 synchronized (registeredSpanNames) { 506 registeredSpanNames.addAll(spanNames); 507 } 508 } 509 510 @Override 511 public void unregisterSpanNamesForCollection(Collection<String> spanNames) { 512 Utils.checkNotNull(spanNames, "spanNames"); 513 synchronized (registeredSpanNames) { 514 registeredSpanNames.removeAll(spanNames); 515 } 516 } 517 518 @Override 519 public Set<String> getRegisteredSpanNamesForCollection() { 520 synchronized (registeredSpanNames) { 521 return Collections.<String>unmodifiableSet(new HashSet<String>(registeredSpanNames)); 522 } 523 } 524 } 525 } 526