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;
|
string requestContent;
|
||||||
|
using (var childSpan = source.StartActivity("ReadStream", ActivityKind.Consumer))
|
||||||
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
||||||
{
|
{
|
||||||
requestContent = reader.ReadToEnd();
|
requestContent = reader.ReadToEnd();
|
||||||
|
childSpan.AddEvent(new ActivityEvent("StreamReader.ReadToEnd"));
|
||||||
}
|
}
|
||||||
|
|
||||||
activity?.AddTag("request.content", requestContent);
|
activity?.AddTag("request.content", requestContent);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
// </copyright>
|
// </copyright>
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace OpenTelemetry.Trace
|
namespace OpenTelemetry.Trace
|
||||||
|
|
@ -31,18 +30,11 @@ namespace OpenTelemetry.Trace
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether activity needs to be created and tracked.
|
/// Checks whether activity needs to be created and tracked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parentContext">Parent activity context. Typically taken from the wire.</param>
|
/// <param name="samplingParameters">
|
||||||
/// <param name="traceId">Trace ID of a activity to be created.</param>
|
/// The <see cref="ActivitySamplingParameters"/> used by the <see cref="ActivitySampler"/>
|
||||||
/// <param name="spanId">Span ID of a activity to be created.</param>
|
/// to decide if the <see cref="Activity"/> to be created is going to be sampled or not.
|
||||||
/// <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>
|
/// </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>
|
/// <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.
|
// This prevents Activity from being created at all.
|
||||||
GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> options) =>
|
GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> options) =>
|
||||||
{
|
{
|
||||||
var shouldSample = sampler.ShouldSample(
|
BuildSamplingParameters(options, out var samplingParameters);
|
||||||
options.Parent,
|
var shouldSample = sampler.ShouldSample(samplingParameters);
|
||||||
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);
|
|
||||||
if (shouldSample.IsSampled)
|
if (shouldSample.IsSampled)
|
||||||
{
|
{
|
||||||
return ActivityDataRequest.AllDataAndRecorded;
|
return ActivityDataRequest.AllDataAndRecorded;
|
||||||
|
|
@ -109,5 +103,35 @@ namespace OpenTelemetry.Trace.Configuration
|
||||||
|
|
||||||
return listener;
|
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
// </copyright>
|
// </copyright>
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace OpenTelemetry.Trace.Samplers
|
namespace OpenTelemetry.Trace.Samplers
|
||||||
{
|
{
|
||||||
|
|
@ -27,7 +25,7 @@ namespace OpenTelemetry.Trace.Samplers
|
||||||
public override string Description { get; } = nameof(AlwaysOffActivitySampler);
|
public override string Description { get; } = nameof(AlwaysOffActivitySampler);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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);
|
return new SamplingResult(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
// </copyright>
|
// </copyright>
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace OpenTelemetry.Trace.Samplers
|
namespace OpenTelemetry.Trace.Samplers
|
||||||
{
|
{
|
||||||
|
|
@ -28,7 +26,7 @@ namespace OpenTelemetry.Trace.Samplers
|
||||||
public override string Description { get; } = nameof(AlwaysOnActivitySampler);
|
public override string Description { get; } = nameof(AlwaysOnActivitySampler);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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);
|
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(
|
Assert.True(
|
||||||
new AlwaysOnActivitySampler()
|
new AlwaysOnActivitySampler()
|
||||||
.ShouldSample(
|
.ShouldSample(new ActivitySamplingParameters(
|
||||||
parentContext,
|
parentContext,
|
||||||
traceId,
|
traceId,
|
||||||
spanId,
|
|
||||||
"Another name",
|
"Another name",
|
||||||
ActivityKindServer,
|
ActivityKindServer,
|
||||||
null,
|
null,
|
||||||
new List<ActivityLink>() { link }).IsSampled);
|
new List<ActivityLink> { link })).IsSampled);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -71,14 +70,13 @@ namespace OpenTelemetry.Trace.Samplers.Test
|
||||||
|
|
||||||
Assert.False(
|
Assert.False(
|
||||||
new AlwaysOffActivitySampler()
|
new AlwaysOffActivitySampler()
|
||||||
.ShouldSample(
|
.ShouldSample(new ActivitySamplingParameters(
|
||||||
parentContext,
|
parentContext,
|
||||||
traceId,
|
traceId,
|
||||||
spanId,
|
|
||||||
"Another name",
|
"Another name",
|
||||||
ActivityKindServer,
|
ActivityKindServer,
|
||||||
null,
|
null,
|
||||||
new List<ActivityLink>() { link }).IsSampled);
|
new List<ActivityLink> { link })).IsSampled);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[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