ParentOrElse Sampler (#852)
* Added ParentOrElseActivitySampler. Updated AlwaysOn, AlwaysOff, & Probability ActivitySamplers for spec changes. * Updated documentation comments. * Added missing braces in ParentOrElse description. Co-authored-by: Cijo Thomas <cithomas@microsoft.com> Co-authored-by: Eddy Nakamura <ednakamu@microsoft.com>
This commit is contained in:
parent
df2d44be07
commit
4c77bc94b3
|
|
@ -22,7 +22,7 @@ namespace OpenTelemetry.Trace.Samplers
|
|||
public sealed class AlwaysOffActivitySampler : ActivitySampler
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Description { get; } = nameof(AlwaysOffActivitySampler);
|
||||
public override string Description { get; } = "AlwaysOffSampler";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SamplingResult ShouldSample(in ActivitySamplingParameters samplingParameters)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace OpenTelemetry.Trace.Samplers
|
|||
public sealed class AlwaysOnActivitySampler : ActivitySampler
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Description { get; } = nameof(AlwaysOnActivitySampler);
|
||||
public override string Description { get; } = "AlwaysOnSampler";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SamplingResult ShouldSample(in ActivitySamplingParameters samplingParameters)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// <copyright file="ParentOrElseActivitySampler.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The 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.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace OpenTelemetry.Trace.Samplers
|
||||
{
|
||||
/// <summary>
|
||||
/// Sampler implementation which will take a sample if parent Activity or any linked Activity is sampled.
|
||||
/// Otherwise, samples root traces according to the specified delegate sampler.
|
||||
/// </summary>
|
||||
public sealed class ParentOrElseActivitySampler : ActivitySampler
|
||||
{
|
||||
private readonly ActivitySampler delegateSampler;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParentOrElseActivitySampler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="delegateSampler">The <see cref="ActivitySampler"/> to be called to decide whether or not to sample a root trace.</param>
|
||||
public ParentOrElseActivitySampler(ActivitySampler delegateSampler)
|
||||
{
|
||||
this.delegateSampler = delegateSampler ?? throw new ArgumentNullException(nameof(delegateSampler));
|
||||
|
||||
this.Description = $"ParentOrElse{{{delegateSampler.Description}}}";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SamplingResult ShouldSample(in ActivitySamplingParameters samplingParameters)
|
||||
{
|
||||
var parentContext = samplingParameters.ParentContext;
|
||||
if (parentContext == default)
|
||||
{
|
||||
// If no parent, use the delegate to determine sampling.
|
||||
return this.delegateSampler.ShouldSample(samplingParameters);
|
||||
}
|
||||
|
||||
// If the parent is sampled keep the sampling decision.
|
||||
if ((parentContext.TraceFlags & ActivityTraceFlags.Recorded) != 0)
|
||||
{
|
||||
return new SamplingResult(true);
|
||||
}
|
||||
|
||||
if (samplingParameters.Links != null)
|
||||
{
|
||||
// If any parent link is sampled keep the sampling decision.
|
||||
foreach (var parentLink in samplingParameters.Links)
|
||||
{
|
||||
if ((parentLink.Context.TraceFlags & ActivityTraceFlags.Recorded) != 0)
|
||||
{
|
||||
return new SamplingResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If parent was not sampled, do not sample.
|
||||
return new SamplingResult(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// <copyright file="ProbabilityActivitySampler.cs" company="OpenTelemetry Authors">
|
||||
// <copyright file="ProbabilityActivitySampler.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
|
@ -14,14 +14,12 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace OpenTelemetry.Trace.Samplers
|
||||
{
|
||||
/// <summary>
|
||||
/// Sampler implementation which will take a sample if parent Activity or any linked Activity is sampled.
|
||||
/// Otherwise, samples traces according to the specified probability.
|
||||
/// Samples traces according to the specified probability.
|
||||
/// </summary>
|
||||
public sealed class ProbabilityActivitySampler : ActivitySampler
|
||||
{
|
||||
|
|
@ -70,25 +68,6 @@ namespace OpenTelemetry.Trace.Samplers
|
|||
/// <inheritdoc />
|
||||
public override SamplingResult ShouldSample(in ActivitySamplingParameters samplingParameters)
|
||||
{
|
||||
// If the parent is sampled keep the sampling decision.
|
||||
var parentContext = samplingParameters.ParentContext;
|
||||
if ((parentContext.TraceFlags & ActivityTraceFlags.Recorded) != 0)
|
||||
{
|
||||
return new SamplingResult(true);
|
||||
}
|
||||
|
||||
if (samplingParameters.Links != null)
|
||||
{
|
||||
// If any parent link is sampled keep the sampling decision.
|
||||
foreach (var parentLink in samplingParameters.Links)
|
||||
{
|
||||
if ((parentLink.Context.TraceFlags & ActivityTraceFlags.Recorded) != 0)
|
||||
{
|
||||
return new SamplingResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always sample if we are within probability range. This is true even for child activities (that
|
||||
// may have had a different sampling decision made) to allow for different sampling policies,
|
||||
// and dynamic increases to sampling probabilities for debugging purposes.
|
||||
|
|
|
|||
|
|
@ -55,9 +55,7 @@ namespace OpenTelemetry.Trace.Samplers.Test
|
|||
[Fact]
|
||||
public void AlwaysOnSampler_GetDescription()
|
||||
{
|
||||
// TODO: The name must be AlwaysOnSampler as per spec.
|
||||
// We should correct it when we replace span sampler with this.
|
||||
Assert.Equal("AlwaysOnActivitySampler", new AlwaysOnActivitySampler().Description);
|
||||
Assert.Equal("AlwaysOnSampler", new AlwaysOnActivitySampler().Description);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -82,9 +80,7 @@ namespace OpenTelemetry.Trace.Samplers.Test
|
|||
[Fact]
|
||||
public void AlwaysOffSampler_GetDescription()
|
||||
{
|
||||
// TODO: The name must be AlwaysOffSampler as per spec.
|
||||
// We should correct it when we replace span sampler with this.
|
||||
Assert.Equal("AlwaysOffActivitySampler", new AlwaysOffActivitySampler().Description);
|
||||
Assert.Equal("AlwaysOffSampler", new AlwaysOffActivitySampler().Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
// <copyright file="ParentOrElseSamplerTests.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The 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.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Trace.Samplers.Test
|
||||
{
|
||||
public class ParentOrElseSamplerTests
|
||||
{
|
||||
private readonly ParentOrElseActivitySampler parentOrElseAlwaysOnSampler = new ParentOrElseActivitySampler(new AlwaysOnActivitySampler());
|
||||
private readonly ParentOrElseActivitySampler parentOrElseAlwaysOffSampler = new ParentOrElseActivitySampler(new AlwaysOffActivitySampler());
|
||||
|
||||
[Fact]
|
||||
public void ParentOrElseSampler_SampledParent()
|
||||
{
|
||||
// No parent, use delegate sampler.
|
||||
Assert.Equal(
|
||||
new SamplingResult(true),
|
||||
this.parentOrElseAlwaysOnSampler.ShouldSample(default));
|
||||
|
||||
// No parent, use delegate sampler.
|
||||
Assert.Equal(
|
||||
new SamplingResult(false),
|
||||
this.parentOrElseAlwaysOffSampler.ShouldSample(default));
|
||||
|
||||
// Not sampled parent, don't sample.
|
||||
Assert.Equal(
|
||||
new SamplingResult(false),
|
||||
this.parentOrElseAlwaysOnSampler.ShouldSample(
|
||||
new ActivitySamplingParameters(
|
||||
parentContext: new ActivityContext(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None),
|
||||
traceId: default,
|
||||
name: "Span",
|
||||
kind: ActivityKind.Client)));
|
||||
|
||||
// Sampled parent, sample.
|
||||
Assert.Equal(
|
||||
new SamplingResult(true),
|
||||
this.parentOrElseAlwaysOffSampler.ShouldSample(
|
||||
new ActivitySamplingParameters(
|
||||
parentContext: new ActivityContext(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.Recorded),
|
||||
traceId: default,
|
||||
name: "Span",
|
||||
kind: ActivityKind.Client)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParentOrElseSampler_SampledParentLink()
|
||||
{
|
||||
var notSampledLink = new ActivityLink[]
|
||||
{
|
||||
new ActivityLink(
|
||||
new ActivityContext(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None)),
|
||||
};
|
||||
|
||||
var sampledLink = new ActivityLink[]
|
||||
{
|
||||
new ActivityLink(
|
||||
new ActivityContext(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.Recorded)),
|
||||
};
|
||||
|
||||
var notSampledParent = new ActivityContext(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None);
|
||||
|
||||
// Not sampled link, don't sample.
|
||||
Assert.Equal(
|
||||
new SamplingResult(false),
|
||||
this.parentOrElseAlwaysOnSampler.ShouldSample(
|
||||
new ActivitySamplingParameters(
|
||||
parentContext: notSampledParent,
|
||||
traceId: default,
|
||||
name: "Span",
|
||||
kind: ActivityKind.Client,
|
||||
links: notSampledLink)));
|
||||
|
||||
// Sampled link, sample.
|
||||
Assert.Equal(
|
||||
new SamplingResult(true),
|
||||
this.parentOrElseAlwaysOffSampler.ShouldSample(
|
||||
new ActivitySamplingParameters(
|
||||
parentContext: notSampledParent,
|
||||
traceId: default,
|
||||
name: "Span",
|
||||
kind: ActivityKind.Client,
|
||||
links: sampledLink)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// <copyright file="ProbabilityActivitySamplerTest.cs" company="OpenTelemetry Authors">
|
||||
// <copyright file="ProbabilityActivitySamplerTest.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -23,21 +22,7 @@ namespace OpenTelemetry.Trace.Samplers.Test
|
|||
public class ProbabilityActivitySamplerTest
|
||||
{
|
||||
private const string ActivityDisplayName = "MyActivityName";
|
||||
private const int NumSampleTries = 1000;
|
||||
private static readonly ActivityKind ActivityKindServer = ActivityKind.Server;
|
||||
private readonly ActivityTraceId traceId;
|
||||
private readonly ActivityContext sampledActivityContext;
|
||||
private readonly ActivityContext notSampledActivityContext;
|
||||
private readonly ActivityLink sampledLink;
|
||||
|
||||
public ProbabilityActivitySamplerTest()
|
||||
{
|
||||
this.traceId = ActivityTraceId.CreateRandom();
|
||||
var parentSpanId = ActivitySpanId.CreateRandom();
|
||||
this.sampledActivityContext = new ActivityContext(this.traceId, parentSpanId, ActivityTraceFlags.Recorded);
|
||||
this.notSampledActivityContext = new ActivityContext(this.traceId, parentSpanId, ActivityTraceFlags.None);
|
||||
this.sampledLink = new ActivityLink(this.sampledActivityContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProbabilitySampler_OutOfRangeHighProbability()
|
||||
|
|
@ -51,66 +36,6 @@ namespace OpenTelemetry.Trace.Samplers.Test
|
|||
Assert.Throws<ArgumentOutOfRangeException>(() => new ProbabilityActivitySampler(-0.00001));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProbabilitySampler_DifferentProbabilities_NotSampledParent()
|
||||
{
|
||||
var neverSample = new ProbabilityActivitySampler(0.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
neverSample, this.notSampledActivityContext, null, 0.0);
|
||||
var alwaysSample = new ProbabilityActivitySampler(1.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
alwaysSample, this.notSampledActivityContext, null, 1.0);
|
||||
var fiftyPercentSample = new ProbabilityActivitySampler(0.5);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
fiftyPercentSample, this.notSampledActivityContext, null, 0.5);
|
||||
var twentyPercentSample = new ProbabilityActivitySampler(0.2);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
twentyPercentSample, this.notSampledActivityContext, null, 0.2);
|
||||
var twoThirdsSample = new ProbabilityActivitySampler(2.0 / 3.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
twoThirdsSample, this.notSampledActivityContext, null, 2.0 / 3.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProbabilitySampler_DifferentProbabilities_SampledParent()
|
||||
{
|
||||
var neverSample = new ProbabilityActivitySampler(0.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
neverSample, this.sampledActivityContext, null, 1.0);
|
||||
var alwaysSample = new ProbabilityActivitySampler(1.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
alwaysSample, this.sampledActivityContext, null, 1.0);
|
||||
var fiftyPercentSample = new ProbabilityActivitySampler(0.5);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
fiftyPercentSample, this.sampledActivityContext, null, 1.0);
|
||||
var twentyPercentSample = new ProbabilityActivitySampler(0.2);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
twentyPercentSample, this.sampledActivityContext, null, 1.0);
|
||||
var twoThirdsSample = new ProbabilityActivitySampler(2.0 / 3.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
twoThirdsSample, this.sampledActivityContext, null, 1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProbabilitySampler_DifferentProbabilities_SampledParentLink()
|
||||
{
|
||||
var neverSample = new ProbabilityActivitySampler(0.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
neverSample, this.notSampledActivityContext, new List<ActivityLink>() { this.sampledLink }, 1.0);
|
||||
var alwaysSample = new ProbabilityActivitySampler(1.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
alwaysSample, this.notSampledActivityContext, new List<ActivityLink>() { this.sampledLink }, 1.0);
|
||||
var fiftyPercentSample = new ProbabilityActivitySampler(0.5);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
fiftyPercentSample, this.notSampledActivityContext, new List<ActivityLink>() { this.sampledLink }, 1.0);
|
||||
var twentyPercentSample = new ProbabilityActivitySampler(0.2);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
twentyPercentSample, this.notSampledActivityContext, new List<ActivityLink>() { this.sampledLink }, 1.0);
|
||||
var twoThirdsSample = new ProbabilityActivitySampler(2.0 / 3.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
twoThirdsSample, this.notSampledActivityContext, new List<ActivityLink>() { this.sampledLink }, 1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProbabilitySampler_SampleBasedOnTraceId()
|
||||
{
|
||||
|
|
@ -187,30 +112,5 @@ namespace OpenTelemetry.Trace.Samplers.Test
|
|||
var expectedDescription = "ProbabilityActivitySampler{0.500000}";
|
||||
Assert.Equal(expectedDescription, new ProbabilityActivitySampler(0.5).Description);
|
||||
}
|
||||
|
||||
// Applies the given sampler to NumSampleTries random traceId/spanId pairs.
|
||||
private static void AssertSamplerSamplesWithProbability(
|
||||
ActivitySampler sampler, ActivityContext parent, List<ActivityLink> links, double probability)
|
||||
{
|
||||
var count = 0; // Count of spans with sampling enabled
|
||||
for (var i = 0; i < NumSampleTries; i++)
|
||||
{
|
||||
if (sampler.ShouldSample(new ActivitySamplingParameters(
|
||||
parent,
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivityDisplayName,
|
||||
ActivityKindServer,
|
||||
null,
|
||||
links)).IsSampled)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
var proportionSampled = (double)count / NumSampleTries;
|
||||
|
||||
// Allow for a large amount of slop (+/- 10%) in number of sampled traces, to avoid flakiness.
|
||||
Assert.True(proportionSampled < probability + 0.1 && proportionSampled > probability - 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue