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.samplers; 18 19 import com.google.auto.value.AutoValue; 20 import io.opencensus.internal.Utils; 21 import io.opencensus.trace.Sampler; 22 import io.opencensus.trace.Span; 23 import io.opencensus.trace.SpanContext; 24 import io.opencensus.trace.SpanId; 25 import io.opencensus.trace.TraceId; 26 import java.util.List; 27 import javax.annotation.Nullable; 28 import javax.annotation.concurrent.Immutable; 29 30 /** 31 * We assume the lower 64 bits of the traceId's are randomly distributed around the whole (long) 32 * range. We convert an incoming probability into an upper bound on that value, such that we can 33 * just compare the absolute value of the id and the bound to see if we are within the desired 34 * probability range. Using the low bits of the traceId also ensures that systems that only use 64 35 * bit ID's will also work with this sampler. 36 */ 37 @AutoValue 38 @Immutable 39 abstract class ProbabilitySampler extends Sampler { 40 ProbabilitySampler()41 ProbabilitySampler() {} 42 getProbability()43 abstract double getProbability(); 44 getIdUpperBound()45 abstract long getIdUpperBound(); 46 47 /** 48 * Returns a new {@link ProbabilitySampler}. The probability of sampling a trace is equal to that 49 * of the specified probability. 50 * 51 * @param probability The desired probability of sampling. Must be within [0.0, 1.0]. 52 * @return a new {@link ProbabilitySampler}. 53 * @throws IllegalArgumentException if {@code probability} is out of range 54 */ create(double probability)55 static ProbabilitySampler create(double probability) { 56 Utils.checkArgument( 57 probability >= 0.0 && probability <= 1.0, "probability must be in range [0.0, 1.0]"); 58 long idUpperBound; 59 // Special case the limits, to avoid any possible issues with lack of precision across 60 // double/long boundaries. For probability == 0.0, we use Long.MIN_VALUE as this guarantees 61 // that we will never sample a trace, even in the case where the id == Long.MIN_VALUE, since 62 // Math.Abs(Long.MIN_VALUE) == Long.MIN_VALUE. 63 if (probability == 0.0) { 64 idUpperBound = Long.MIN_VALUE; 65 } else if (probability == 1.0) { 66 idUpperBound = Long.MAX_VALUE; 67 } else { 68 idUpperBound = (long) (probability * Long.MAX_VALUE); 69 } 70 return new AutoValue_ProbabilitySampler(probability, idUpperBound); 71 } 72 73 @Override shouldSample( @ullable SpanContext parentContext, @Nullable Boolean hasRemoteParent, TraceId traceId, SpanId spanId, String name, @Nullable List<Span> parentLinks)74 public final boolean shouldSample( 75 @Nullable SpanContext parentContext, 76 @Nullable Boolean hasRemoteParent, 77 TraceId traceId, 78 SpanId spanId, 79 String name, 80 @Nullable List<Span> parentLinks) { 81 // If the parent is sampled keep the sampling decision. 82 if (parentContext != null && parentContext.getTraceOptions().isSampled()) { 83 return true; 84 } 85 if (parentLinks != null) { 86 // If any parent link is sampled keep the sampling decision. 87 for (Span parentLink : parentLinks) { 88 if (parentLink.getContext().getTraceOptions().isSampled()) { 89 return true; 90 } 91 } 92 } 93 // Always sample if we are within probability range. This is true even for child spans (that 94 // may have had a different sampling decision made) to allow for different sampling policies, 95 // and dynamic increases to sampling probabilities for debugging purposes. 96 // Note use of '<' for comparison. This ensures that we never sample for probability == 0.0, 97 // while allowing for a (very) small chance of *not* sampling if the id == Long.MAX_VALUE. 98 // This is considered a reasonable tradeoff for the simplicity/performance requirements (this 99 // code is executed in-line for every Span creation). 100 return Math.abs(traceId.getLowerLong()) < getIdUpperBound(); 101 } 102 103 @Override getDescription()104 public final String getDescription() { 105 return String.format("ProbabilitySampler{%.6f}", getProbability()); 106 } 107 } 108