-
Notifications
You must be signed in to change notification settings - Fork 859
Added AlwaysRecordSampler #6732
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # OpenTelemetry .NET Diagnostic: OTEL1005 | ||
|
|
||
| ## Overview | ||
|
|
||
| This is an experimental API for allowing spans to always be recorded. | ||
|
|
||
| ### Details | ||
|
|
||
| #### AlwaysRecordSampler | ||
|
|
||
| TODO: Explanation. | ||
|
|
||
| **Parameters:** | ||
|
|
||
| * TODO: Details | ||
| * `span` - a read/write span object for the span which is about to be ended. | ||
|
|
||
| **Returns:** `TODO` | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -12,6 +12,9 @@ Notes](../../RELEASENOTES.md). | |||||
| * Added support for `Meter.TelemetrySchemaUrl` property. | ||||||
| ([#6714](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6714)) | ||||||
|
|
||||||
| * Added `AlwaysRecordSampler`. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| ([#6732](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6732)) | ||||||
|
|
||||||
| ## 1.14.0 | ||||||
|
|
||||||
| Released 2025-Nov-12 | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,70 @@ | ||||||||||||||||
| // Copyright The OpenTelemetry Authors | ||||||||||||||||
| // SPDX-License-Identifier: Apache-2.0 | ||||||||||||||||
|
|
||||||||||||||||
| // Includes work from: | ||||||||||||||||
| // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||||||||||||||||
| // SPDX-License-Identifier: Apache-2.0 | ||||||||||||||||
|
|
||||||||||||||||
| #if EXPOSE_EXPERIMENTAL_FEATURES | ||||||||||||||||
| using System.Diagnostics.CodeAnalysis; | ||||||||||||||||
| #endif | ||||||||||||||||
| using OpenTelemetry.Internal; | ||||||||||||||||
|
|
||||||||||||||||
| namespace OpenTelemetry.Trace; | ||||||||||||||||
|
|
||||||||||||||||
| #if EXPOSE_EXPERIMENTAL_FEATURES | ||||||||||||||||
| /// <summary> | ||||||||||||||||
| /// This sampler will return the sampling result of the provided rootSampler, unless the | ||||||||||||||||
| /// sampling result contains the sampling decision <see cref="SamplingDecision.Drop"/>, in which case, a | ||||||||||||||||
| /// new sampling result will be returned that is functionally equivalent to the original, except that | ||||||||||||||||
| /// it contains the sampling decision <see cref="SamplingDecision.RecordOnly"/>. This ensures that all | ||||||||||||||||
| /// spans are recorded, with no change to sampling. | ||||||||||||||||
| /// | ||||||||||||||||
| /// The intended use case of this sampler is to provide a means of sending all spans to a | ||||||||||||||||
| /// processor without having an impact on the sampling rate. This may be desirable if a user wishes | ||||||||||||||||
| /// to count or otherwise measure all spans produced in a service, without incurring the cost of 100% | ||||||||||||||||
| /// sampling. | ||||||||||||||||
| /// </summary> | ||||||||||||||||
| [Experimental(DiagnosticDefinitions.AlwaysRecordSamplerExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] | ||||||||||||||||
| public | ||||||||||||||||
| #else | ||||||||||||||||
| internal | ||||||||||||||||
| #endif | ||||||||||||||||
| sealed class AlwaysRecordSampler : Sampler | ||||||||||||||||
| { | ||||||||||||||||
| private readonly Sampler rootSampler; | ||||||||||||||||
|
|
||||||||||||||||
| private AlwaysRecordSampler(Sampler rootSampler) | ||||||||||||||||
| { | ||||||||||||||||
| this.rootSampler = rootSampler; | ||||||||||||||||
| this.Description = "AlwaysRecordSampler{" + rootSampler.Description + "}"; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /// <summary> | ||||||||||||||||
| /// Method to create an AlwaysRecordSampler. | ||||||||||||||||
| /// </summary> | ||||||||||||||||
| /// <param name="rootSampler"><see cref="Sampler"/>rootSampler to create AlwaysRecordSampler from.</param> | ||||||||||||||||
| /// <returns>Created AlwaysRecordSampler.</returns> | ||||||||||||||||
| public static AlwaysRecordSampler Create(Sampler rootSampler) | ||||||||||||||||
| { | ||||||||||||||||
| Guard.ThrowIfNull(rootSampler); | ||||||||||||||||
| return new AlwaysRecordSampler(rootSampler); | ||||||||||||||||
|
Check failure on line 51 in src/OpenTelemetry/Trace/Sampler/AlwaysRecordSampler.cs
|
||||||||||||||||
|
Comment on lines
+50
to
+51
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same problem we have in the ParentBasedSample
Suggested change
|
||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /// <inheritdoc/> | ||||||||||||||||
| public override SamplingResult ShouldSample(in SamplingParameters samplingParameters) | ||||||||||||||||
| { | ||||||||||||||||
| SamplingResult result = this.rootSampler.ShouldSample(samplingParameters); | ||||||||||||||||
| if (result.Decision == SamplingDecision.Drop) | ||||||||||||||||
| { | ||||||||||||||||
| result = WrapResultWithRecordOnlyResult(result); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| return result; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| private static SamplingResult WrapResultWithRecordOnlyResult(SamplingResult result) | ||||||||||||||||
| { | ||||||||||||||||
| return new SamplingResult(SamplingDecision.RecordOnly, result.Attributes, result.TraceStateString); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| // Copyright The OpenTelemetry Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| // Includes work from: | ||
| // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| using System.Diagnostics; | ||
| using OpenTelemetry.Tests; | ||
| using Xunit; | ||
|
|
||
| namespace OpenTelemetry.Trace.Tests; | ||
|
|
||
| /// <summary> | ||
| /// AlwaysRecordSamplerTest test class. | ||
| /// </summary> | ||
| public class AlwaysRecordSamplerTests | ||
| { | ||
| /// <summary> | ||
| /// Tests Description is set properly with AlwaysRecordSampler keyword. | ||
| /// </summary> | ||
| [Fact] | ||
| public void TestGetDescription() | ||
| { | ||
| var testSampler = new TestSampler(); | ||
| var sampler = AlwaysRecordSampler.Create(testSampler); | ||
| Assert.Equal("AlwaysRecordSampler{TestSampler}", sampler.Description); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test RECORD_AND_SAMPLE sampling decision. | ||
| /// </summary> | ||
| [Fact] | ||
| public void TestRecordAndSampleSamplingDecision() | ||
| { | ||
| ValidateShouldSample(SamplingDecision.RecordAndSample, SamplingDecision.RecordAndSample); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test RECORD_ONLY sampling decision. | ||
| /// </summary> | ||
| [Fact] | ||
| public void TestRecordOnlySamplingDecision() | ||
| { | ||
| ValidateShouldSample(SamplingDecision.RecordOnly, SamplingDecision.RecordOnly); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test DROP sampling decision. | ||
| /// </summary> | ||
| [Fact] | ||
| public void TestDropSamplingDecision() | ||
| { | ||
| ValidateShouldSample(SamplingDecision.Drop, SamplingDecision.RecordOnly); | ||
| } | ||
|
|
||
| private static SamplingResult BuildRootSamplingResult(SamplingDecision samplingDecision) | ||
| { | ||
| ActivityTagsCollection? attributes = new ActivityTagsCollection | ||
| { | ||
| { "key", samplingDecision.GetType().Name }, | ||
| }; | ||
| string traceState = samplingDecision.GetType().Name; | ||
| #pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types. | ||
| return new SamplingResult(samplingDecision, attributes, traceState); | ||
| #pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types. | ||
| } | ||
|
|
||
| private static void ValidateShouldSample( | ||
| SamplingDecision rootDecision, SamplingDecision expectedDecision) | ||
| { | ||
| SamplingResult rootResult = BuildRootSamplingResult(rootDecision); | ||
| var testSampler = new TestSampler { SamplingAction = _ => rootResult }; | ||
| var sampler = AlwaysRecordSampler.Create(testSampler); | ||
|
|
||
| SamplingParameters samplingParameters = new SamplingParameters( | ||
| default, default, "name", ActivityKind.Client, new ActivityTagsCollection(), new List<ActivityLink>()); | ||
|
|
||
| SamplingResult actualResult = sampler.ShouldSample(samplingParameters); | ||
|
|
||
| if (rootDecision.Equals(expectedDecision)) | ||
| { | ||
| Assert.True(actualResult.Equals(rootResult)); | ||
| Assert.True(actualResult.Decision.Equals(rootDecision)); | ||
| } | ||
| else | ||
| { | ||
| Assert.False(actualResult.Equals(rootResult)); | ||
| Assert.True(actualResult.Decision.Equals(expectedDecision)); | ||
| } | ||
|
|
||
| Assert.Equal(rootResult.Attributes, actualResult.Attributes); | ||
| Assert.Equal(rootDecision.GetType().Name, actualResult.TraceStateString); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs some real data here.