All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.opentelemetry.sdk.trace.Samplers Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019, OpenTelemetry 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.opentelemetry.sdk.trace;

import static io.opentelemetry.common.AttributeValue.doubleAttributeValue;

import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import io.opentelemetry.common.Attributes;
import io.opentelemetry.common.ReadableAttributes;
import io.opentelemetry.sdk.trace.Sampler.Decision;
import io.opentelemetry.sdk.trace.Sampler.SamplingResult;
import io.opentelemetry.trace.Link;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.SpanContext;
import io.opentelemetry.trace.TraceId;
import io.opentelemetry.trace.attributes.DoubleAttributeSetter;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

/**
 * Static class to access a set of pre-defined {@link Sampler Samplers}.
 *
 * @since 0.1.0
 */
@Immutable
public final class Samplers {

  /**
   * Probability value used by a probability-based Span sampling strategy.
   *
   * 

Note: This will need to be updated if a specification for this value is merged which changes * this proposed value. Also, once it's in the spec, we should move it somewhere more visible. * *

See https://github.com/open-telemetry/opentelemetry-specification/pull/570 */ // Visible for tests. static final DoubleAttributeSetter SAMPLING_PROBABILITY = DoubleAttributeSetter.create("sampling.probability"); private static final SamplingResult EMPTY_RECORDED_AND_SAMPLED_SAMPLING_RESULT = SamplingResultImpl.createWithoutAttributes(Decision.RECORD_AND_SAMPLED); private static final SamplingResult EMPTY_NOT_SAMPLED_OR_RECORDED_SAMPLING_RESULT = SamplingResultImpl.createWithoutAttributes(Decision.NOT_RECORD); private static final SamplingResult EMPTY_RECORDED_SAMPLING_RESULT = SamplingResultImpl.createWithoutAttributes(Decision.RECORD); // No instance of this class. private Samplers() {} static boolean isRecording(Decision decision) { return Decision.RECORD.equals(decision) || Decision.RECORD_AND_SAMPLED.equals(decision); } static boolean isSampled(Decision decision) { return Decision.RECORD_AND_SAMPLED.equals(decision); } /** * Returns a {@link SamplingResult} with the given {@code attributes} and {@link * SamplingResult#getDecision()} returning {@code decision}. * *

This is meant for use by custom {@link Sampler} implementations. * *

Using {@link #emptySamplingResult(Decision)} instead of this method is slightly faster and * shorter if you don't need attributes. * * @param decision The decision made on the span. * @param attributes The attributes to return from {@link SamplingResult#getAttributes()}. A * different object instance with the same elements may be returned. * @return A {@link SamplingResult} with the attributes equivalent to {@code attributes} and the * provided {@code decision}. */ public static SamplingResult samplingResult(Decision decision, Attributes attributes) { Objects.requireNonNull(attributes, "attributes"); return attributes.isEmpty() ? emptySamplingResult(decision) : SamplingResultImpl.create(decision, attributes); } /** * Returns a {@link SamplingResult} with empty attributes and {@link SamplingResult#getDecision()} * returning {@code decision}. * *

This is meant for use by custom {@link Sampler} implementations. * *

Use {@link #samplingResult(Decision, Attributes)} if you need attributes. * * @param decision The decision made on the span. * @return A {@link SamplingResult} with empty attributes and the provided {@code decision}. */ public static SamplingResult emptySamplingResult(Decision decision) { switch (decision) { case RECORD_AND_SAMPLED: return EMPTY_RECORDED_AND_SAMPLED_SAMPLING_RESULT; case RECORD: return EMPTY_RECORDED_SAMPLING_RESULT; case NOT_RECORD: return EMPTY_NOT_SAMPLED_OR_RECORDED_SAMPLING_RESULT; } throw new AssertionError("unrecognised samplingResult"); } /** * Returns a {@link Sampler} that always makes a "yes" {@link SamplingResult} for {@link Span} * sampling. * * @return a {@code Sampler} that always makes a "yes" {@link SamplingResult} for {@code Span} * sampling. * @since 0.1.0 */ public static Sampler alwaysOn() { return AlwaysOnSampler.INSTANCE; } /** * Returns a {@link Sampler} that always makes a "no" {@link SamplingResult} for {@link Span} * sampling. * * @return a {@code Sampler} that always makes a "no" {@link SamplingResult} for {@code Span} * sampling. * @since 0.1.0 */ public static Sampler alwaysOff() { return AlwaysOffSampler.INSTANCE; } /** * Returns a {@link Sampler} that always makes the same decision as the parent {@link Span} to * whether or not to sample. If there is no parent, the Sampler uses the provided Sampler delegate * to determine the sampling decision. * * @param delegateSampler the {@code Sampler} which is used to make the sampling decisions if the * parent does not exist. * @return a {@code Sampler} that follows the parent's sampling decision if one exists, otherwise * following the delegate sampler's decision. * @since 0.7.0 */ public static Sampler parentOrElse(Sampler delegateSampler) { return new ParentOrElse(delegateSampler); } /** * Returns a new Probability {@link Sampler}. The probability of sampling a trace is equal to that * of the specified probability. * * @param probability The desired probability of sampling. Must be within [0.0, 1.0]. * @return a new Probability {@link Sampler}. * @throws IllegalArgumentException if {@code probability} is out of range */ public static Sampler probability(double probability) { return Probability.create(probability); } @Immutable private enum AlwaysOnSampler implements Sampler { INSTANCE; // Returns a "yes" {@link SamplingResult} for {@link Span} sampling. @Override public SamplingResult shouldSample( @Nullable SpanContext parentContext, TraceId traceId, String name, Kind spanKind, ReadableAttributes attributes, List parentLinks) { return EMPTY_RECORDED_AND_SAMPLED_SAMPLING_RESULT; } @Override public String getDescription() { return "AlwaysOnSampler"; } } @Immutable private enum AlwaysOffSampler implements Sampler { INSTANCE; // Returns a "no" {@link SamplingResult}T on {@link Span} sampling. @Override public SamplingResult shouldSample( @Nullable SpanContext parentContext, TraceId traceId, String name, Kind spanKind, ReadableAttributes attributes, List parentLinks) { return EMPTY_NOT_SAMPLED_OR_RECORDED_SAMPLING_RESULT; } @Override public String getDescription() { return "AlwaysOffSampler"; } } @Immutable static class ParentOrElse implements Sampler { private final Sampler delegateSampler; ParentOrElse(Sampler delegateSampler) { this.delegateSampler = delegateSampler; } // If a parent is set, always follows the same sampling decision as the parent. // Otherwise, uses the delegateSampler provided at initialization to make a decision. @Override public SamplingResult shouldSample( @Nullable SpanContext parentContext, TraceId traceId, String name, Kind spanKind, ReadableAttributes attributes, List parentLinks) { if (parentContext != null) { if (parentContext.getTraceFlags().isSampled()) { return EMPTY_RECORDED_AND_SAMPLED_SAMPLING_RESULT; } return EMPTY_NOT_SAMPLED_OR_RECORDED_SAMPLING_RESULT; } return this.delegateSampler.shouldSample( parentContext, traceId, name, spanKind, attributes, parentLinks); } @Override public String getDescription() { return String.format("ParentOrElse{%s}", this.delegateSampler.getDescription()); } } /** * We assume the lower 64 bits of the traceId's are randomly distributed around the whole (long) * range. We convert an incoming probability into an upper bound on that value, such that we can * just compare the absolute value of the id and the bound to see if we are within the desired * probability range. Using the low bits of the traceId also ensures that systems that only use 64 * bit ID's will also work with this sampler. */ @AutoValue @Immutable abstract static class Probability implements Sampler { Probability() {} static Probability create(double probability) { Preconditions.checkArgument( probability >= 0.0 && probability <= 1.0, "probability must be in range [0.0, 1.0]"); long idUpperBound; // Special case the limits, to avoid any possible issues with lack of precision across // double/long boundaries. For probability == 0.0, we use Long.MIN_VALUE as this guarantees // that we will never sample a trace, even in the case where the id == Long.MIN_VALUE, since // Math.Abs(Long.MIN_VALUE) == Long.MIN_VALUE. if (probability == 0.0) { idUpperBound = Long.MIN_VALUE; } else if (probability == 1.0) { idUpperBound = Long.MAX_VALUE; } else { idUpperBound = (long) (probability * Long.MAX_VALUE); } return new AutoValue_Samplers_Probability( probability, idUpperBound, SamplingResultImpl.createWithProbability(Decision.RECORD_AND_SAMPLED, probability), SamplingResultImpl.createWithProbability(Decision.NOT_RECORD, probability)); } abstract double getProbability(); abstract long getIdUpperBound(); abstract SamplingResult getPositiveSamplingResult(); abstract SamplingResult getNegativeSamplingResult(); @Override public final SamplingResult shouldSample( @Nullable SpanContext parentContext, TraceId traceId, String name, Kind spanKind, ReadableAttributes attributes, @Nullable List parentLinks) { // If the parent is sampled keep the sampling samplingResult. if (parentContext != null && parentContext.getTraceFlags().isSampled()) { return EMPTY_RECORDED_AND_SAMPLED_SAMPLING_RESULT; } if (parentLinks != null) { // If any parent link is sampled keep the sampling samplingResult. for (Link parentLink : parentLinks) { if (parentLink.getContext().getTraceFlags().isSampled()) { return EMPTY_RECORDED_AND_SAMPLED_SAMPLING_RESULT; } } } // Always sample if we are within probability range. This is true even for child spans (that // may have had a different sampling samplingResult made) to allow for different sampling // policies, // and dynamic increases to sampling probabilities for debugging purposes. // Note use of '<' for comparison. This ensures that we never sample for probability == 0.0, // while allowing for a (very) small chance of *not* sampling if the id == Long.MAX_VALUE. // This is considered a reasonable tradeoff for the simplicity/performance requirements (this // code is executed in-line for every Span creation). return Math.abs(traceId.getTraceRandomPart()) < getIdUpperBound() ? getPositiveSamplingResult() : getNegativeSamplingResult(); } @Override public final String getDescription() { return String.format("ProbabilitySampler{%.6f}", getProbability()); } } @Immutable @AutoValue abstract static class SamplingResultImpl implements SamplingResult { /** * Creates sampling result with probability attribute. * * @param decision the decision on sampling and recording * @param probability the probability that was used for the samplingResult. */ static SamplingResult createWithProbability(Decision decision, double probability) { return new AutoValue_Samplers_SamplingResultImpl( decision, Attributes.of(SAMPLING_PROBABILITY.key(), doubleAttributeValue(probability))); } /** * Creates sampling result without attributes. * * @param decision sampling samplingResult */ static SamplingResult createWithoutAttributes(Decision decision) { return new AutoValue_Samplers_SamplingResultImpl(decision, Attributes.empty()); } /** * Creates sampling result with the given attributes. * * @param decision sampling decisionq * @param attributes attributes. Will not be copied, so do not modify afterwards. */ static SamplingResult create(Decision decision, Attributes attributes) { return new AutoValue_Samplers_SamplingResultImpl(decision, attributes); } @Override public abstract Decision getDecision(); @Override public abstract Attributes getAttributes(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy