// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics; using OpenTelemetry.Internal; namespace OpenTelemetry.Trace; /// /// Sampler implementation which by default will take a sample if parent Activity is sampled. /// Otherwise, samples root traces according to the specified root sampler. /// /// /// The default behavior can be customized by providing additional samplers to be invoked for different /// combinations of local/remote parent and its sampling decision. /// See . /// public sealed class ParentBasedSampler : Sampler { private readonly Sampler rootSampler; private readonly Sampler remoteParentSampled; private readonly Sampler remoteParentNotSampled; private readonly Sampler localParentSampled; private readonly Sampler localParentNotSampled; /// /// Initializes a new instance of the class. /// /// The to be called for root span/activity. public ParentBasedSampler(Sampler rootSampler) { Guard.ThrowIfNull(rootSampler); this.rootSampler = rootSampler; #pragma warning disable CA1062 // Validate arguments of public methods - needed for netstandard2.1 this.Description = $"ParentBased{{{rootSampler.Description}}}"; #pragma warning restore CA1062 // Validate arguments of public methods - needed for netstandard2.1 this.remoteParentSampled = AlwaysOnSampler.Instance; this.remoteParentNotSampled = AlwaysOffSampler.Instance; this.localParentSampled = AlwaysOnSampler.Instance; this.localParentNotSampled = AlwaysOffSampler.Instance; } /// /// Initializes a new instance of the class with ability to delegate /// sampling decision to one of the inner samplers provided. /// /// The to be called for root span/activity. /// /// A to delegate sampling decision to in case of /// remote parent ( == true) with flag == true. /// Default: . /// /// /// A to delegate sampling decision to in case of /// remote parent ( == true) with flag == false. /// Default: . /// /// /// A to delegate sampling decision to in case of /// local parent ( == false) with flag == true. /// Default: . /// /// /// A to delegate sampling decision to in case of /// local parent ( == false) with flag == false. /// Default: . /// public ParentBasedSampler( Sampler rootSampler, Sampler? remoteParentSampled = null, Sampler? remoteParentNotSampled = null, Sampler? localParentSampled = null, Sampler? localParentNotSampled = null) : this(rootSampler) { this.remoteParentSampled = remoteParentSampled ?? AlwaysOnSampler.Instance; this.remoteParentNotSampled = remoteParentNotSampled ?? AlwaysOffSampler.Instance; this.localParentSampled = localParentSampled ?? AlwaysOnSampler.Instance; this.localParentNotSampled = localParentNotSampled ?? AlwaysOffSampler.Instance; } /// public override SamplingResult ShouldSample(in SamplingParameters samplingParameters) { var parentContext = samplingParameters.ParentContext; if (parentContext.TraceId == default) { // If no parent, use the rootSampler to determine sampling. return this.rootSampler.ShouldSample(samplingParameters); } // Is parent sampled? if ((parentContext.TraceFlags & ActivityTraceFlags.Recorded) != 0) { return parentContext.IsRemote ? this.remoteParentSampled.ShouldSample(samplingParameters) : this.localParentSampled.ShouldSample(samplingParameters); } // If parent is not sampled => delegate to the "not sampled" inner samplers. return parentContext.IsRemote ? this.remoteParentNotSampled.ShouldSample(samplingParameters) : this.localParentNotSampled.ShouldSample(samplingParameters); } }