Add Probability Sampler for Activity (#702)
* Merging changes in OpenTelemetrySdk * Renames from span to activity
This commit is contained in:
parent
0867c66a0f
commit
296e0ff3a7
|
|
@ -77,9 +77,11 @@ namespace Samples
|
|||
}
|
||||
|
||||
string requestContent;
|
||||
using (var childSpan = source.StartActivity("ReadStream", ActivityKind.Consumer))
|
||||
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
||||
{
|
||||
requestContent = reader.ReadToEnd();
|
||||
childSpan.AddEvent(new ActivityEvent("StreamReader.ReadToEnd"));
|
||||
}
|
||||
|
||||
activity?.AddTag("request.content", requestContent);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
|
|
@ -31,18 +30,11 @@ namespace OpenTelemetry.Trace
|
|||
/// <summary>
|
||||
/// Checks whether activity needs to be created and tracked.
|
||||
/// </summary>
|
||||
/// <param name="parentContext">Parent activity context. Typically taken from the wire.</param>
|
||||
/// <param name="traceId">Trace ID of a activity to be created.</param>
|
||||
/// <param name="spanId">Span ID of a activity to be created.</param>
|
||||
/// <param name="name"> Name (DisplayName) of the activity to be created. Note, that the name of the activity is settable.
|
||||
/// So this name can be changed later and Sampler implementation should assume that.
|
||||
/// Typical example of a name change is when <see cref="Activity"/> representing incoming http request
|
||||
/// has a name of url path and then being updated with route name when routing complete.
|
||||
/// <param name="samplingParameters">
|
||||
/// The <see cref="ActivitySamplingParameters"/> used by the <see cref="ActivitySampler"/>
|
||||
/// to decide if the <see cref="Activity"/> to be created is going to be sampled or not.
|
||||
/// </param>
|
||||
/// <param name="activityKind">The kind of the Activity.</param>
|
||||
/// <param name="tags">Initial set of Tags for the Activity being constructed.</param>
|
||||
/// <param name="links">Links associated with the activity.</param>
|
||||
/// <returns>Sampling decision on whether activity needs to be sampled or not.</returns>
|
||||
public abstract SamplingResult ShouldSample(in ActivityContext parentContext, in ActivityTraceId traceId, in ActivitySpanId spanId, string name, ActivityKind activityKind, IEnumerable<KeyValuePair<string, string>> tags, IEnumerable<ActivityLink> links);
|
||||
public abstract SamplingResult ShouldSample(in ActivitySamplingParameters samplingParameters);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
// <copyright file="ActivitySamplingParameters.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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
/// <summary>
|
||||
/// Sampling parameters passed to an <see cref="ActivitySampler"/> for it to make a sampling decision.
|
||||
/// </summary>
|
||||
public readonly struct ActivitySamplingParameters
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActivitySamplingParameters"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="parentContext">Parent activity context. Typically taken from the wire.</param>
|
||||
/// <param name="traceId">Trace ID of a activity to be created.</param>
|
||||
/// <param name="name">The name (DisplayName) of the activity to be created. Note, that the name of the activity is settable.
|
||||
/// So this name can be changed later and Sampler implementation should assume that.
|
||||
/// Typical example of a name change is when <see cref="Activity"/> representing incoming http request
|
||||
/// has a name of url path and then being updated with route name when routing complete.
|
||||
/// </param>
|
||||
/// <param name="kind">The kind of the Activity to be created.</param>
|
||||
/// <param name="tags">Initial set of Tags for the Activity being constructed.</param>
|
||||
/// <param name="links">Links associated with the activity.</param>
|
||||
public ActivitySamplingParameters(
|
||||
ActivityContext parentContext,
|
||||
ActivityTraceId traceId,
|
||||
string name,
|
||||
ActivityKind kind,
|
||||
IEnumerable<KeyValuePair<string, string>> tags = null, // TODO: Empty
|
||||
IEnumerable<ActivityLink> links = null)
|
||||
{
|
||||
this.ParentContext = parentContext;
|
||||
this.TraceId = traceId;
|
||||
this.Name = name;
|
||||
this.Kind = kind;
|
||||
this.Tags = tags;
|
||||
this.Links = links;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent activity context.
|
||||
/// </summary>
|
||||
public ActivityContext ParentContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trace ID of parent activity or a new generated one for root span/activity.
|
||||
/// </summary>
|
||||
public ActivityTraceId TraceId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name to be given to the span/activity.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the kind of span/activity to be created.
|
||||
/// </summary>
|
||||
public ActivityKind Kind { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tags to be associated to the span/activity to be created.
|
||||
/// </summary>
|
||||
public IEnumerable<KeyValuePair<string, string>> Tags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the links to be added to the activity to be created.
|
||||
/// </summary>
|
||||
public IEnumerable<ActivityLink> Links { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -84,14 +84,8 @@ namespace OpenTelemetry.Trace.Configuration
|
|||
// This prevents Activity from being created at all.
|
||||
GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> options) =>
|
||||
{
|
||||
var shouldSample = sampler.ShouldSample(
|
||||
options.Parent,
|
||||
options.Parent.TraceId,
|
||||
spanId: default, // Passing default SpanId here. The actual SpanId is not known before actual Activity creation
|
||||
options.Name,
|
||||
options.Kind,
|
||||
options.Tags,
|
||||
options.Links);
|
||||
BuildSamplingParameters(options, out var samplingParameters);
|
||||
var shouldSample = sampler.ShouldSample(samplingParameters);
|
||||
if (shouldSample.IsSampled)
|
||||
{
|
||||
return ActivityDataRequest.AllDataAndRecorded;
|
||||
|
|
@ -109,5 +103,35 @@ namespace OpenTelemetry.Trace.Configuration
|
|||
|
||||
return listener;
|
||||
}
|
||||
|
||||
internal static void BuildSamplingParameters(
|
||||
in ActivityCreationOptions<ActivityContext> options, out ActivitySamplingParameters samplingParameters)
|
||||
{
|
||||
ActivityContext parentContext = options.Parent;
|
||||
if (parentContext == default)
|
||||
{
|
||||
// Check if there is already a parent for the current activity.
|
||||
var parentActivity = Activity.Current;
|
||||
if (parentActivity != null)
|
||||
{
|
||||
parentContext = parentActivity.Context;
|
||||
}
|
||||
}
|
||||
|
||||
// This is not going to be the final traceId of the Activity (if one is created), however, it is
|
||||
// needed in order for the sampling to work. This differs from other OTel SDKs in which it is
|
||||
// the Sampler always receives the actual traceId of a root span/activity.
|
||||
ActivityTraceId traceId = parentContext.TraceId != default
|
||||
? parentContext.TraceId
|
||||
: ActivityTraceId.CreateRandom();
|
||||
|
||||
samplingParameters = new ActivitySamplingParameters(
|
||||
parentContext,
|
||||
traceId,
|
||||
options.Name,
|
||||
options.Kind,
|
||||
options.Tags,
|
||||
options.Links);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace OpenTelemetry.Trace.Samplers
|
||||
{
|
||||
|
|
@ -27,7 +25,7 @@ namespace OpenTelemetry.Trace.Samplers
|
|||
public override string Description { get; } = nameof(AlwaysOffActivitySampler);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SamplingResult ShouldSample(in ActivityContext parentContext, in ActivityTraceId traceId, in ActivitySpanId spanId, string name, ActivityKind activityKind, IEnumerable<KeyValuePair<string, string>> tags, IEnumerable<ActivityLink> links)
|
||||
public override SamplingResult ShouldSample(in ActivitySamplingParameters samplingParameters)
|
||||
{
|
||||
return new SamplingResult(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace OpenTelemetry.Trace.Samplers
|
||||
{
|
||||
|
|
@ -28,7 +26,7 @@ namespace OpenTelemetry.Trace.Samplers
|
|||
public override string Description { get; } = nameof(AlwaysOnActivitySampler);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SamplingResult ShouldSample(in ActivityContext parentContext, in ActivityTraceId traceId, in ActivitySpanId spanId, string name, ActivityKind activityKind, IEnumerable<KeyValuePair<string, string>> tags, IEnumerable<ActivityLink> links)
|
||||
public override SamplingResult ShouldSample(in ActivitySamplingParameters samplingParameters)
|
||||
{
|
||||
return new SamplingResult(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
// <copyright file="ProbabilityActivitySampler.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;
|
||||
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.
|
||||
/// </summary>
|
||||
public sealed class ProbabilityActivitySampler : ActivitySampler
|
||||
{
|
||||
private readonly long idUpperBound;
|
||||
private readonly double probability;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProbabilityActivitySampler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="probability">The desired probability of sampling. This must be between 0.0 and 1.0.
|
||||
/// Higher the value, higher is the probability of a given Activity to be sampled in.
|
||||
/// </param>
|
||||
public ProbabilityActivitySampler(double probability)
|
||||
{
|
||||
if (probability < 0.0 || probability > 1.0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be in range [0.0, 1.0]");
|
||||
}
|
||||
|
||||
this.probability = probability;
|
||||
|
||||
// The expected description is like ProbabilityActivitySampler{0.000100}
|
||||
this.Description = "ProbabilityActivitySampler{" + this.probability.ToString("F6", CultureInfo.InvariantCulture) + "}";
|
||||
|
||||
// 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 (this.probability == 0.0)
|
||||
{
|
||||
this.idUpperBound = long.MinValue;
|
||||
}
|
||||
else if (this.probability == 1.0)
|
||||
{
|
||||
this.idUpperBound = long.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.idUpperBound = (long)(probability * long.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description { get; }
|
||||
|
||||
/// <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.
|
||||
// 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 trade-off for the simplicity/performance requirements (this
|
||||
// code is executed in-line for every Activity creation).
|
||||
Span<byte> traceIdBytes = stackalloc byte[16];
|
||||
samplingParameters.TraceId.CopyTo(traceIdBytes);
|
||||
return Math.Abs(this.GetLowerLong(traceIdBytes)) < this.idUpperBound ? new SamplingResult(true) : new SamplingResult(false);
|
||||
}
|
||||
|
||||
private long GetLowerLong(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
long result = 0;
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
result <<= 8;
|
||||
#pragma warning disable CS0675 // Bitwise-or operator used on a sign-extended operand
|
||||
result |= bytes[i] & 0xff;
|
||||
#pragma warning restore CS0675 // Bitwise-or operator used on a sign-extended operand
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// <copyright file="ActivityListenerSdkTest.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.Diagnostics;
|
||||
using OpenTelemetry.Trace;
|
||||
using OpenTelemetry.Trace.Configuration;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Tests.Implementation.Trace
|
||||
{
|
||||
public class ActivityListenerSdkTest
|
||||
{
|
||||
static ActivityListenerSdkTest()
|
||||
{
|
||||
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
|
||||
Activity.ForceDefaultIdFormat = true;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSamplingParametersHandlesCurrentActivity()
|
||||
{
|
||||
using var activitySource = new ActivitySource(nameof(BuildSamplingParametersHandlesCurrentActivity));
|
||||
|
||||
var latestSamplingParameters = new ActivitySamplingParameters();
|
||||
|
||||
using var listener = new ActivityListener
|
||||
{
|
||||
ShouldListenTo = _ => true,
|
||||
GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> options) =>
|
||||
{
|
||||
OpenTelemetrySdk.BuildSamplingParameters(options, out latestSamplingParameters);
|
||||
return ActivityDataRequest.AllDataAndRecorded;
|
||||
},
|
||||
};
|
||||
|
||||
ActivitySource.AddActivityListener(listener);
|
||||
|
||||
using (var root = activitySource.StartActivity("root"))
|
||||
{
|
||||
Assert.Equal(default(ActivitySpanId), root.ParentSpanId);
|
||||
|
||||
// This enforces the current behavior that the traceId passed to the sampler for the
|
||||
// root span/activity is not the traceId actually used.
|
||||
Assert.NotEqual(root.TraceId, latestSamplingParameters.TraceId);
|
||||
}
|
||||
|
||||
using (var parent = activitySource.StartActivity("parent", ActivityKind.Client))
|
||||
{
|
||||
// This enforces the current behavior that the traceId passed to the sampler for the
|
||||
// root span/activity is not the traceId actually used.
|
||||
Assert.NotEqual(parent.TraceId, latestSamplingParameters.TraceId);
|
||||
using (var child = activitySource.StartActivity("child"))
|
||||
{
|
||||
Assert.Equal(parent.TraceId, latestSamplingParameters.TraceId);
|
||||
Assert.Equal(parent.TraceId, child.TraceId);
|
||||
Assert.Equal(parent.SpanId, child.ParentSpanId);
|
||||
}
|
||||
}
|
||||
|
||||
var customContext = new ActivityContext(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None);
|
||||
using (var fromCustomContext =
|
||||
activitySource.StartActivity("customContext", ActivityKind.Client, customContext))
|
||||
{
|
||||
Assert.Equal(customContext.TraceId, fromCustomContext.TraceId);
|
||||
Assert.Equal(customContext.SpanId, fromCustomContext.ParentSpanId);
|
||||
Assert.NotEqual(customContext.SpanId, fromCustomContext.SpanId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,14 +43,13 @@ namespace OpenTelemetry.Trace.Samplers.Test
|
|||
|
||||
Assert.True(
|
||||
new AlwaysOnActivitySampler()
|
||||
.ShouldSample(
|
||||
.ShouldSample(new ActivitySamplingParameters(
|
||||
parentContext,
|
||||
traceId,
|
||||
spanId,
|
||||
"Another name",
|
||||
ActivityKindServer,
|
||||
null,
|
||||
new List<ActivityLink>() { link }).IsSampled);
|
||||
new List<ActivityLink> { link })).IsSampled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -71,14 +70,13 @@ namespace OpenTelemetry.Trace.Samplers.Test
|
|||
|
||||
Assert.False(
|
||||
new AlwaysOffActivitySampler()
|
||||
.ShouldSample(
|
||||
.ShouldSample(new ActivitySamplingParameters(
|
||||
parentContext,
|
||||
traceId,
|
||||
spanId,
|
||||
"Another name",
|
||||
ActivityKindServer,
|
||||
null,
|
||||
new List<ActivityLink>() { link }).IsSampled);
|
||||
new List<ActivityLink> { link })).IsSampled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,212 @@
|
|||
// <copyright file="ProbabilityActivitySamplerTest.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;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
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()
|
||||
{
|
||||
traceId = ActivityTraceId.CreateRandom();
|
||||
var parentSpanId = ActivitySpanId.CreateRandom();
|
||||
sampledActivityContext = new ActivityContext(traceId, parentSpanId, ActivityTraceFlags.Recorded);
|
||||
notSampledActivityContext = new ActivityContext(traceId, parentSpanId, ActivityTraceFlags.None);
|
||||
sampledLink = new ActivityLink(this.sampledActivityContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProbabilitySampler_OutOfRangeHighProbability()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new ProbabilityActivitySampler(1.01));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProbabilitySampler_OutOfRangeLowProbability()
|
||||
{
|
||||
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>() { sampledLink }, 1.0);
|
||||
var alwaysSample = new ProbabilityActivitySampler(1.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
alwaysSample, this.notSampledActivityContext, new List<ActivityLink>() { sampledLink }, 1.0);
|
||||
var fiftyPercentSample = new ProbabilityActivitySampler(0.5);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
fiftyPercentSample, this.notSampledActivityContext, new List<ActivityLink>() { sampledLink }, 1.0);
|
||||
var twentyPercentSample = new ProbabilityActivitySampler(0.2);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
twentyPercentSample, this.notSampledActivityContext, new List<ActivityLink>() { sampledLink }, 1.0);
|
||||
var twoThirdsSample = new ProbabilityActivitySampler(2.0 / 3.0);
|
||||
AssertSamplerSamplesWithProbability(
|
||||
twoThirdsSample, this.notSampledActivityContext, new List<ActivityLink>() { sampledLink }, 1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProbabilitySampler_SampleBasedOnTraceId()
|
||||
{
|
||||
ActivitySampler defaultProbability = new ProbabilityActivitySampler(0.0001);
|
||||
// This traceId will not be sampled by the ProbabilityActivitySampler because the first 8 bytes as long
|
||||
// is not less than probability * Long.MAX_VALUE;
|
||||
var notSampledtraceId =
|
||||
ActivityTraceId.CreateFromBytes(
|
||||
new byte[]
|
||||
{
|
||||
0x8F,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
});
|
||||
Assert.False(
|
||||
defaultProbability.ShouldSample(new ActivitySamplingParameters(
|
||||
default,
|
||||
notSampledtraceId,
|
||||
ActivityDisplayName,
|
||||
ActivityKindServer,
|
||||
null,
|
||||
null)).IsSampled);
|
||||
// This traceId will be sampled by the ProbabilityActivitySampler because the first 8 bytes as long
|
||||
// is less than probability * Long.MAX_VALUE;
|
||||
var sampledtraceId =
|
||||
ActivityTraceId.CreateFromBytes(
|
||||
new byte[]
|
||||
{
|
||||
0x00,
|
||||
0x00,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
});
|
||||
Assert.True(
|
||||
defaultProbability.ShouldSample(new ActivitySamplingParameters(
|
||||
default,
|
||||
sampledtraceId,
|
||||
ActivityDisplayName,
|
||||
ActivityKindServer,
|
||||
null,
|
||||
null)).IsSampled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProbabilitySampler_GetDescription()
|
||||
{
|
||||
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