Skip to content

Commit

Permalink
Merge branch 'master' into cijothomas/activity3adapter1a_alternate
Browse files Browse the repository at this point in the history
  • Loading branch information
cijothomas committed Jun 2, 2020
2 parents 0832e71 + 296e0ff commit 3b4cbb7
Show file tree
Hide file tree
Showing 10 changed files with 545 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ public void Start(string url)
}
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);
Expand Down
16 changes: 4 additions & 12 deletions src/OpenTelemetry/Trace/ActivitySampler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -31,18 +30,11 @@ public abstract class ActivitySampler
/// <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);
}
}
85 changes: 85 additions & 0 deletions src/OpenTelemetry/Trace/ActivitySamplingParameters.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
40 changes: 32 additions & 8 deletions src/OpenTelemetry/Trace/Configuration/OpenTelemetrySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,8 @@ public static IDisposable EnableOpenTelemetry(Action<OpenTelemetryBuilder> confi
// 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;
Expand All @@ -109,5 +103,35 @@ public static IDisposable EnableOpenTelemetry(Action<OpenTelemetryBuilder> confi

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);
}
}
}
4 changes: 1 addition & 3 deletions src/OpenTelemetry/Trace/Samplers/AlwaysOffActivitySampler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -27,7 +25,7 @@ public sealed class AlwaysOffActivitySampler : ActivitySampler
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);
}
Expand Down
4 changes: 1 addition & 3 deletions src/OpenTelemetry/Trace/Samplers/AlwaysOnActivitySampler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -28,7 +26,7 @@ public sealed class AlwaysOnActivitySampler : ActivitySampler
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);
}
Expand Down
118 changes: 118 additions & 0 deletions src/OpenTelemetry/Trace/Samplers/ProbabilityActivitySampler.cs
Original file line number Diff line number Diff line change
@@ -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 comments on commit 3b4cbb7

Please sign in to comment.