Added sampler to Activity - v1 (#683)

* Added sampler to Activity
This commit is contained in:
Cijo Thomas 2020-05-20 22:47:07 -07:00 committed by GitHub
parent 5ed0e452d1
commit 577ae6cce3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 266 additions and 2 deletions

View File

@ -49,6 +49,12 @@ namespace Samples
if (parent != null)
{
parent.DisplayName = "HttpIn DisplayName";
// IsAllDataRequested is equivalent of Span.IsRecording
if (parent.IsAllDataRequested)
{
parent.AddTag("expensive data", "This data is expensive to obtain. Avoid it if activity is not being recorded");
}
}
try

View File

@ -0,0 +1,48 @@
// <copyright file="ActivitySampler.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>
/// Sampler to select data to be exported. This sampler executes before Activity object is created.
/// </summary>
public abstract class ActivitySampler
{
/// <summary>
/// Gets the sampler description.
/// </summary>
public abstract string Description { get; }
/// <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>
/// <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);
}
}

View File

@ -32,6 +32,8 @@ namespace OpenTelemetry.Trace.Configuration
internal ActivityProcessorPipelineBuilder ProcessingPipeline { get; private set; }
internal ActivitySampler Sampler { get; private set; }
internal HashSet<string> ActivitySourceNames { get; private set; }
/// <summary>
@ -52,6 +54,17 @@ namespace OpenTelemetry.Trace.Configuration
return this;
}
/// <summary>
/// Configures sampler.
/// </summary>
/// <param name="sampler">Sampler instance.</param>
/// <returns>Returns <see cref="OpenTelemetryBuilder"/> for chaining.</returns>
public OpenTelemetryBuilder SetSampler(ActivitySampler sampler)
{
this.Sampler = sampler ?? throw new ArgumentNullException(nameof(sampler));
return this;
}
/// <summary>
/// Adds given activitysource name to the list of subscribed sources.
/// </summary>

View File

@ -17,6 +17,7 @@
using System;
using System.Diagnostics;
using OpenTelemetry.Trace.Export;
using OpenTelemetry.Trace.Samplers;
namespace OpenTelemetry.Trace.Configuration
{
@ -40,6 +41,8 @@ namespace OpenTelemetry.Trace.Configuration
var openTelemetryBuilder = new OpenTelemetryBuilder();
configureOpenTelemetryBuilder(openTelemetryBuilder);
ActivitySampler sampler = openTelemetryBuilder.Sampler ?? new AlwaysOnActivitySampler();
ActivityProcessor activityProcessor;
if (openTelemetryBuilder.ProcessingPipeline == null)
{
@ -65,9 +68,40 @@ namespace OpenTelemetry.Trace.Configuration
// or not
ShouldListenTo = (activitySource) => openTelemetryBuilder.ActivitySourceNames.Contains(activitySource.Name.ToUpperInvariant()),
// The following parameters are not used now.
// The following parameter is not used now.
GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> options) => ActivityDataRequest.AllData,
GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> options) => ActivityDataRequest.AllData,
// This delegate informs ActivitySource about sampling decision.
// Following simple behavior is enabled now:
// If Sampler returns IsSampled as true, returns ActivityDataRequest.AllDataAndRecorded
// This creates Activity and sets its IsAllDataRequested to true.
// Library authors can check activity.IsAllDataRequested and avoid
// doing any additional telemetry population.
// Activity.IsAllDataRequested is the equivalent of Span.IsRecording
//
// If Sampler returns IsSampled as false, returns ActivityDataRequest.None
// This prevents Activity from being created at all.
GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> options) =>
{
var shouldSample = sampler.ShouldSample(
options.Parent,
options.Parent.TraceId,
default(ActivitySpanId), // 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)
{
return ActivityDataRequest.AllDataAndRecorded;
}
else
{
return ActivityDataRequest.None;
}
// TODO: Improve this to properly use ActivityDataRequest.AllData, PropagationData as well.
},
};
ActivitySource.AddActivityListener(listener);

View File

@ -0,0 +1,35 @@
// <copyright file="AlwaysOffActivitySampler.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.Samplers
{
/// <summary>
/// Sampler implementation which never samples any activity.
/// </summary>
public sealed class AlwaysOffActivitySampler : ActivitySampler
{
/// <inheritdoc />
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)
{
return new SamplingResult(false);
}
}
}

View File

@ -0,0 +1,36 @@
// <copyright file="AlwaysOnActivitySampler.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.Samplers
{
/// <summary>
/// Sampler implementation which samples every activity.
/// This sampler will be used as the default Sampler, if no other Sampler is configured.
/// </summary>
public sealed class AlwaysOnActivitySampler : ActivitySampler
{
/// <inheritdoc />
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)
{
return new SamplingResult(true);
}
}
}

View File

@ -0,0 +1,92 @@
// <copyright file="ActivitySamplersTest.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;
using Xunit;
namespace OpenTelemetry.Trace.Samplers.Test
{
public class ActivitySamplersTest
{
private static readonly ActivityKind ActivityKindServer = ActivityKind.Server;
private readonly ActivityTraceId traceId;
private readonly ActivitySpanId spanId;
private readonly ActivitySpanId parentSpanId;
public ActivitySamplersTest()
{
traceId = ActivityTraceId.CreateRandom();
spanId = ActivitySpanId.CreateRandom();
parentSpanId = ActivitySpanId.CreateRandom();
}
[Theory]
[InlineData(ActivityTraceFlags.Recorded)]
[InlineData(ActivityTraceFlags.None)]
public void AlwaysOnSampler_AlwaysReturnTrue(ActivityTraceFlags flags)
{
var parentContext = new ActivityContext(traceId, parentSpanId, flags);
var link = new ActivityLink(parentContext);
Assert.True(
new AlwaysOnActivitySampler()
.ShouldSample(
parentContext,
traceId,
spanId,
"Another name",
ActivityKindServer,
null,
new List<ActivityLink>() { link }).IsSampled);
}
[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);
}
[Theory]
[InlineData(ActivityTraceFlags.Recorded)]
[InlineData(ActivityTraceFlags.None)]
public void AlwaysOffSampler_AlwaysReturnFalse(ActivityTraceFlags flags)
{
var parentContext = new ActivityContext(traceId, parentSpanId, flags);
var link = new ActivityLink(parentContext);
Assert.False(
new AlwaysOffActivitySampler()
.ShouldSample(
parentContext,
traceId,
spanId,
"Another name",
ActivityKindServer,
null,
new List<ActivityLink>() { link }).IsSampled);
}
[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);
}
}
}