1202 lines
52 KiB
C#
1202 lines
52 KiB
C#
// <copyright file="TracerProviderSdkTest.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.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using OpenTelemetry.Instrumentation;
|
|
using OpenTelemetry.Resources;
|
|
using OpenTelemetry.Tests;
|
|
using Xunit;
|
|
|
|
namespace OpenTelemetry.Trace.Tests
|
|
{
|
|
public class TracerProviderSdkTest : IDisposable
|
|
{
|
|
private const string ActivitySourceName = "TraceSdkTest";
|
|
|
|
public TracerProviderSdkTest()
|
|
{
|
|
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerProviderSdkAddSource()
|
|
{
|
|
using var source1 = new ActivitySource($"{Utils.GetCurrentMethodName()}.1");
|
|
using var source2 = new ActivitySource($"{Utils.GetCurrentMethodName()}.2");
|
|
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource(source1.Name)
|
|
.Build();
|
|
|
|
using (var activity = source1.StartActivity("test"))
|
|
{
|
|
Assert.NotNull(activity);
|
|
}
|
|
|
|
using (var activity = source2.StartActivity("test"))
|
|
{
|
|
Assert.Null(activity);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerProviderSdkAddSourceWithWildcards()
|
|
{
|
|
using var source1 = new ActivitySource($"{Utils.GetCurrentMethodName()}.A");
|
|
using var source2 = new ActivitySource($"{Utils.GetCurrentMethodName()}.Ab");
|
|
using var source3 = new ActivitySource($"{Utils.GetCurrentMethodName()}.Abc");
|
|
using var source4 = new ActivitySource($"{Utils.GetCurrentMethodName()}.B");
|
|
|
|
using (var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource($"{Utils.GetCurrentMethodName()}.*")
|
|
.Build())
|
|
{
|
|
using (var activity = source1.StartActivity("test"))
|
|
{
|
|
Assert.NotNull(activity);
|
|
}
|
|
|
|
using (var activity = source2.StartActivity("test"))
|
|
{
|
|
Assert.NotNull(activity);
|
|
}
|
|
|
|
using (var activity = source3.StartActivity("test"))
|
|
{
|
|
Assert.NotNull(activity);
|
|
}
|
|
|
|
using (var activity = source4.StartActivity("test"))
|
|
{
|
|
Assert.NotNull(activity);
|
|
}
|
|
}
|
|
|
|
using (var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource($"{Utils.GetCurrentMethodName()}.?")
|
|
.Build())
|
|
{
|
|
using (var activity = source1.StartActivity("test"))
|
|
{
|
|
Assert.NotNull(activity);
|
|
}
|
|
|
|
using (var activity = source2.StartActivity("test"))
|
|
{
|
|
Assert.Null(activity);
|
|
}
|
|
|
|
using (var activity = source3.StartActivity("test"))
|
|
{
|
|
Assert.Null(activity);
|
|
}
|
|
|
|
using (var activity = source4.StartActivity("test"))
|
|
{
|
|
Assert.NotNull(activity);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerProviderSdkInvokesSamplingWithCorrectParameters()
|
|
{
|
|
var testSampler = new TestSampler();
|
|
using var activitySource = new ActivitySource(ActivitySourceName);
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource(ActivitySourceName)
|
|
.SetSampler(testSampler)
|
|
.Build();
|
|
|
|
// OpenTelemetry Sdk is expected to set default to W3C.
|
|
Assert.True(Activity.DefaultIdFormat == ActivityIdFormat.W3C);
|
|
|
|
using (var rootActivity = activitySource.StartActivity("root"))
|
|
{
|
|
Assert.NotNull(rootActivity);
|
|
Assert.True(rootActivity.ParentSpanId == default);
|
|
|
|
// Validate that the TraceId seen by Sampler is same as the
|
|
// Activity when it got created.
|
|
Assert.Equal(rootActivity.TraceId, testSampler.LatestSamplingParameters.TraceId);
|
|
}
|
|
|
|
using (var parent = activitySource.StartActivity("parent", ActivityKind.Client))
|
|
{
|
|
Assert.Equal(parent.TraceId, testSampler.LatestSamplingParameters.TraceId);
|
|
using var child = activitySource.StartActivity("child");
|
|
Assert.Equal(child.TraceId, testSampler.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(fromCustomContext.TraceId, testSampler.LatestSamplingParameters.TraceId);
|
|
Assert.Equal(customContext.TraceId, fromCustomContext.TraceId);
|
|
Assert.Equal(customContext.SpanId, fromCustomContext.ParentSpanId);
|
|
Assert.NotEqual(customContext.SpanId, fromCustomContext.SpanId);
|
|
}
|
|
|
|
// Validate that when StartActivity is called using Parent as string,
|
|
// Sampling is called correctly.
|
|
var act = new Activity("anything").Start();
|
|
act.Stop();
|
|
var customContextAsString = act.Id;
|
|
var expectedTraceId = act.TraceId;
|
|
var expectedParentSpanId = act.SpanId;
|
|
|
|
using (var fromCustomContextAsString =
|
|
activitySource.StartActivity("customContext", ActivityKind.Client, customContextAsString))
|
|
{
|
|
Assert.Equal(fromCustomContextAsString.TraceId, testSampler.LatestSamplingParameters.TraceId);
|
|
Assert.Equal(expectedTraceId, fromCustomContextAsString.TraceId);
|
|
Assert.Equal(expectedParentSpanId, fromCustomContextAsString.ParentSpanId);
|
|
}
|
|
|
|
// Verify that StartActivity returns an instance of Activity.
|
|
using var fromInvalidW3CIdParent =
|
|
activitySource.StartActivity("customContext", ActivityKind.Client, "InvalidW3CIdParent");
|
|
Assert.NotNull(fromInvalidW3CIdParent);
|
|
|
|
// Verify that the TestSampler was invoked and received the correct params.
|
|
Assert.Equal(fromInvalidW3CIdParent.TraceId, testSampler.LatestSamplingParameters.TraceId);
|
|
|
|
// OpenTelemetry ActivityContext does not support non W3C Ids.
|
|
Assert.Null(fromInvalidW3CIdParent.ParentId);
|
|
Assert.Equal(default, fromInvalidW3CIdParent.ParentSpanId);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(SamplingDecision.Drop)]
|
|
[InlineData(SamplingDecision.RecordOnly)]
|
|
[InlineData(SamplingDecision.RecordAndSample)]
|
|
public void TracerProviderSdkSamplerAttributesAreAppliedToActivity(SamplingDecision sampling)
|
|
{
|
|
var testSampler = new TestSampler
|
|
{
|
|
SamplingAction = (samplingParams) =>
|
|
{
|
|
var attributes = new Dictionary<string, object>
|
|
{
|
|
{ "tagkeybysampler", "tagvalueaddedbysampler" },
|
|
};
|
|
return new SamplingResult(sampling, attributes);
|
|
},
|
|
};
|
|
|
|
using var activitySource = new ActivitySource(ActivitySourceName);
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource(ActivitySourceName)
|
|
.SetSampler(testSampler)
|
|
.Build();
|
|
|
|
using var rootActivity = activitySource.StartActivity("root");
|
|
Assert.NotNull(rootActivity);
|
|
Assert.Equal(rootActivity.TraceId, testSampler.LatestSamplingParameters.TraceId);
|
|
if (sampling != SamplingDecision.Drop)
|
|
{
|
|
Assert.Contains(new KeyValuePair<string, object>("tagkeybysampler", "tagvalueaddedbysampler"), rootActivity.TagObjects);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerSdkSetsActivitySamplingResultAsPropagationWhenParentIsRemote()
|
|
{
|
|
var testSampler = new TestSampler();
|
|
using var activitySource = new ActivitySource(ActivitySourceName);
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource(ActivitySourceName)
|
|
.SetSampler(testSampler)
|
|
.Build();
|
|
|
|
testSampler.SamplingAction = (samplingParameters) =>
|
|
{
|
|
return new SamplingResult(SamplingDecision.Drop);
|
|
};
|
|
|
|
ActivityContext ctx = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None, isRemote: true);
|
|
|
|
using (var activity = activitySource.StartActivity("root", ActivityKind.Server, ctx))
|
|
{
|
|
// Even if sampling returns false, for activities with remote parent,
|
|
// activity is still created with PropagationOnly.
|
|
Assert.NotNull(activity);
|
|
Assert.False(activity.IsAllDataRequested);
|
|
Assert.False(activity.Recorded);
|
|
|
|
// This is not a root activity and parent is not remote.
|
|
// If sampling returns false, no activity is created at all.
|
|
using var innerActivity = activitySource.StartActivity("inner");
|
|
Assert.Null(innerActivity);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerSdkSetsActivitySamplingResultBasedOnSamplingDecision()
|
|
{
|
|
var testSampler = new TestSampler();
|
|
using var activitySource = new ActivitySource(ActivitySourceName);
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource(ActivitySourceName)
|
|
.SetSampler(testSampler)
|
|
.Build();
|
|
|
|
testSampler.SamplingAction = (samplingParameters) =>
|
|
{
|
|
return new SamplingResult(SamplingDecision.RecordAndSample);
|
|
};
|
|
|
|
using (var activity = activitySource.StartActivity("root"))
|
|
{
|
|
Assert.NotNull(activity);
|
|
Assert.True(activity.IsAllDataRequested);
|
|
Assert.True(activity.Recorded);
|
|
}
|
|
|
|
testSampler.SamplingAction = (samplingParameters) =>
|
|
{
|
|
return new SamplingResult(SamplingDecision.RecordOnly);
|
|
};
|
|
|
|
using (var activity = activitySource.StartActivity("root"))
|
|
{
|
|
// Even if sampling returns false, for root activities,
|
|
// activity is still created with PropagationOnly.
|
|
Assert.NotNull(activity);
|
|
Assert.True(activity.IsAllDataRequested);
|
|
Assert.False(activity.Recorded);
|
|
}
|
|
|
|
testSampler.SamplingAction = (samplingParameters) =>
|
|
{
|
|
return new SamplingResult(SamplingDecision.Drop);
|
|
};
|
|
|
|
using (var activity = activitySource.StartActivity("root"))
|
|
{
|
|
// Even if sampling returns false, for root activities,
|
|
// activity is still created with PropagationOnly.
|
|
Assert.NotNull(activity);
|
|
Assert.False(activity.IsAllDataRequested);
|
|
Assert.False(activity.Recorded);
|
|
|
|
// This is not a root activity.
|
|
// If sampling returns false, no activity is created at all.
|
|
using var innerActivity = activitySource.StartActivity("inner");
|
|
Assert.Null(innerActivity);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerSdkSetsActivitySamplingResultToNoneWhenSuppressInstrumentationIsTrue()
|
|
{
|
|
using var scope = SuppressInstrumentationScope.Begin();
|
|
|
|
var testSampler = new TestSampler();
|
|
using var activitySource = new ActivitySource(ActivitySourceName);
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource(ActivitySourceName)
|
|
.SetSampler(testSampler)
|
|
.Build();
|
|
|
|
using var activity = activitySource.StartActivity("root");
|
|
Assert.Null(activity);
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerSdkSetsActivityDataRequestedToFalseWhenSuppressInstrumentationIsTrueForLegacyActivity()
|
|
{
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
bool startCalled = false;
|
|
bool endCalled = false;
|
|
|
|
testActivityProcessor.StartAction =
|
|
(a) =>
|
|
{
|
|
startCalled = true;
|
|
};
|
|
|
|
testActivityProcessor.EndAction =
|
|
(a) =>
|
|
{
|
|
endCalled = true;
|
|
};
|
|
|
|
using var openTelemetry = Sdk.CreateTracerProviderBuilder()
|
|
.AddLegacySource("random")
|
|
.AddProcessor(testActivityProcessor)
|
|
.SetSampler(new AlwaysOnSampler())
|
|
.Build();
|
|
|
|
using (SuppressInstrumentationScope.Begin(true))
|
|
{
|
|
using var activity = new Activity("random").Start();
|
|
Assert.False(activity.IsAllDataRequested);
|
|
}
|
|
|
|
Assert.False(startCalled);
|
|
Assert.False(endCalled);
|
|
}
|
|
|
|
[Fact]
|
|
public void ProcessorDoesNotReceiveNotRecordDecisionSpan()
|
|
{
|
|
var testSampler = new TestSampler();
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
bool startCalled = false;
|
|
bool endCalled = false;
|
|
|
|
testActivityProcessor.StartAction =
|
|
(a) =>
|
|
{
|
|
startCalled = true;
|
|
};
|
|
|
|
testActivityProcessor.EndAction =
|
|
(a) =>
|
|
{
|
|
endCalled = true;
|
|
};
|
|
|
|
using var openTelemetry = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource("random")
|
|
.AddProcessor(testActivityProcessor)
|
|
.SetSampler(testSampler)
|
|
.Build();
|
|
|
|
testSampler.SamplingAction = (samplingParameters) =>
|
|
{
|
|
return new SamplingResult(SamplingDecision.Drop);
|
|
};
|
|
|
|
using ActivitySource source = new ActivitySource("random");
|
|
var activity = source.StartActivity("somename");
|
|
activity.Stop();
|
|
|
|
Assert.False(activity.IsAllDataRequested);
|
|
Assert.Equal(ActivityTraceFlags.None, activity.ActivityTraceFlags);
|
|
Assert.False(activity.Recorded);
|
|
Assert.False(startCalled);
|
|
Assert.False(endCalled);
|
|
}
|
|
|
|
// Test to check that TracerProvider does not call Processor.OnStart or Processor.OnEnd for a legacy activity when no legacy OperationName is
|
|
// provided to TracerProviderBuilder.
|
|
[Fact]
|
|
public void SdkDoesNotProcessLegacyActivityWithNoAdditionalConfig()
|
|
{
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
bool startCalled = false;
|
|
bool endCalled = false;
|
|
|
|
testActivityProcessor.StartAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnStart is called, activity's IsAllDataRequested is set to true
|
|
startCalled = true;
|
|
};
|
|
|
|
testActivityProcessor.EndAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
|
endCalled = true;
|
|
};
|
|
|
|
var emptyActivitySource = new ActivitySource(string.Empty);
|
|
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
|
|
|
// No AddLegacyOperationName chained to TracerProviderBuilder
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddProcessor(testActivityProcessor)
|
|
.Build();
|
|
|
|
Assert.False(emptyActivitySource.HasListeners()); // No listener for empty ActivitySource even after build
|
|
|
|
Activity activity = new Activity("Test");
|
|
activity.Start();
|
|
activity.Stop();
|
|
|
|
Assert.False(startCalled); // Processor.OnStart is not called since we did not add any legacy OperationName
|
|
Assert.False(endCalled); // Processor.OnEnd is not called since we did not add any legacy OperationName
|
|
}
|
|
|
|
// Test to check that TracerProvider samples a legacy activity using a custom Sampler and calls Processor.OnStart and Processor.OnEnd for the
|
|
// legacy activity when the correct legacy OperationName is provided to TracerProviderBuilder.
|
|
[Fact]
|
|
public void SdkSamplesAndProcessesLegacyActivityWithRightConfig()
|
|
{
|
|
bool samplerCalled = false;
|
|
|
|
var sampler = new TestSampler
|
|
{
|
|
SamplingAction =
|
|
(samplingParameters) =>
|
|
{
|
|
samplerCalled = true;
|
|
return new SamplingResult(SamplingDecision.RecordAndSample);
|
|
},
|
|
};
|
|
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
bool startCalled = false;
|
|
bool endCalled = false;
|
|
|
|
testActivityProcessor.StartAction =
|
|
(a) =>
|
|
{
|
|
Assert.True(samplerCalled);
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnStart is called, activity's IsAllDataRequested is set to true
|
|
startCalled = true;
|
|
};
|
|
|
|
testActivityProcessor.EndAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
|
endCalled = true;
|
|
};
|
|
|
|
var emptyActivitySource = new ActivitySource(string.Empty);
|
|
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
|
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
|
|
// AddLegacyOperationName chained to TracerProviderBuilder
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(sampler)
|
|
.AddProcessor(testActivityProcessor)
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
Assert.True(emptyActivitySource.HasListeners()); // Listener for empty ActivitySource added after TracerProvider build
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
activity.Stop();
|
|
|
|
Assert.True(startCalled); // Processor.OnStart is called since we added a legacy OperationName
|
|
Assert.True(endCalled); // Processor.OnEnd is called since we added a legacy OperationName
|
|
}
|
|
|
|
// Test to check that TracerProvider samples a legacy activity using a custom Sampler and calls Processor.OnStart and Processor.OnEnd for the
|
|
// legacy activity when the correct legacy OperationName is provided to TracerProviderBuilder and a wildcard Source is added
|
|
[Fact]
|
|
public void SdkSamplesAndProcessesLegacyActivityWithRightConfigOnWildCardMode()
|
|
{
|
|
bool samplerCalled = false;
|
|
|
|
var sampler = new TestSampler
|
|
{
|
|
SamplingAction =
|
|
(samplingParameters) =>
|
|
{
|
|
samplerCalled = true;
|
|
return new SamplingResult(SamplingDecision.RecordAndSample);
|
|
},
|
|
};
|
|
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
bool startCalled = false;
|
|
bool endCalled = false;
|
|
|
|
testActivityProcessor.StartAction =
|
|
(a) =>
|
|
{
|
|
Assert.True(samplerCalled);
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnStart is called, activity's IsAllDataRequested is set to true
|
|
startCalled = true;
|
|
};
|
|
|
|
testActivityProcessor.EndAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
|
endCalled = true;
|
|
};
|
|
|
|
var emptyActivitySource = new ActivitySource(string.Empty);
|
|
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
|
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
|
|
// AddLegacyOperationName chained to TracerProviderBuilder
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(sampler)
|
|
.AddSource("ABCCompany.XYZProduct.*") // Adding a wild card source
|
|
.AddProcessor(testActivityProcessor)
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
Assert.True(emptyActivitySource.HasListeners()); // Listener for empty ActivitySource added after TracerProvider build
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
activity.Stop();
|
|
|
|
Assert.True(startCalled); // Processor.OnStart is called since we added a legacy OperationName
|
|
Assert.True(endCalled); // Processor.OnEnd is called since we added a legacy OperationName
|
|
}
|
|
|
|
// Test to check that TracerProvider does not call Processor.OnEnd for a legacy activity whose ActivitySource got updated before Activity.Stop and
|
|
// the updated source was not added to the Provider
|
|
[Fact]
|
|
public void SdkCallsOnlyProcessorOnStartForLegacyActivityWhenActivitySourceIsUpdatedWithoutAddSource()
|
|
{
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
bool startCalled = false;
|
|
bool endCalled = false;
|
|
|
|
testActivityProcessor.StartAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnStart is called, activity's IsAllDataRequested is set to true
|
|
startCalled = true;
|
|
};
|
|
|
|
testActivityProcessor.EndAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
|
endCalled = true;
|
|
};
|
|
|
|
var emptyActivitySource = new ActivitySource(string.Empty);
|
|
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
|
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
var activitySourceForLegacyActivity = new ActivitySource("TestActivitySource", "1.0.0");
|
|
|
|
// AddLegacyOperationName chained to TracerProviderBuilder
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.AddProcessor(testActivityProcessor)
|
|
.Build();
|
|
|
|
Assert.True(emptyActivitySource.HasListeners()); // Listener for empty ActivitySource added after TracerProvider build
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, activitySourceForLegacyActivity);
|
|
activity.Stop();
|
|
|
|
Assert.True(startCalled); // Processor.OnStart is called since we provided the legacy OperationName
|
|
Assert.False(endCalled); // Processor.OnEnd is not called since the ActivitySource is updated and the updated source name is not added as a Source to the provider
|
|
}
|
|
|
|
// Test to check that TracerProvider calls Processor.OnStart and Processor.OnEnd for a legacy activity whose ActivitySource got updated before Activity.Stop and
|
|
// the updated source was added to the Provider
|
|
[Fact]
|
|
public void SdkProcessesLegacyActivityWhenActivitySourceIsUpdatedWithAddSource()
|
|
{
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
bool startCalled = false;
|
|
bool endCalled = false;
|
|
|
|
testActivityProcessor.StartAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnStart is called, activity's IsAllDataRequested is set to true
|
|
startCalled = true;
|
|
};
|
|
|
|
testActivityProcessor.EndAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
|
endCalled = true;
|
|
};
|
|
|
|
var emptyActivitySource = new ActivitySource(string.Empty);
|
|
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
|
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
var activitySourceForLegacyActivity = new ActivitySource("TestActivitySource", "1.0.0");
|
|
|
|
// AddLegacyOperationName chained to TracerProviderBuilder
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddSource(activitySourceForLegacyActivity.Name) // Add the updated ActivitySource as a Source
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.AddProcessor(testActivityProcessor)
|
|
.Build();
|
|
|
|
Assert.True(emptyActivitySource.HasListeners()); // Listener for empty ActivitySource added after TracerProvider build
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, activitySourceForLegacyActivity);
|
|
activity.Stop();
|
|
|
|
Assert.True(startCalled); // Processor.OnStart is called since we provided the legacy OperationName
|
|
Assert.True(endCalled); // Processor.OnEnd is not called since the ActivitySource is updated and the updated source name is added as a Source to the provider
|
|
}
|
|
|
|
// Test to check that TracerProvider continues to process legacy activities even after a new Processor is added after the building the provider.
|
|
[Fact]
|
|
public void SdkProcessesLegacyActivityEvenAfterAddingNewProcessor()
|
|
{
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
bool startCalled = false;
|
|
bool endCalled = false;
|
|
|
|
testActivityProcessor.StartAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnStart is called, activity's IsAllDataRequested is set to true
|
|
startCalled = true;
|
|
};
|
|
|
|
testActivityProcessor.EndAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
|
endCalled = true;
|
|
};
|
|
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
|
|
// AddLegacyOperationName chained to TracerProviderBuilder
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddProcessor(testActivityProcessor)
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
Assert.Equal(tracerProvider, testActivityProcessor.ParentProvider);
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
activity.Stop();
|
|
|
|
Assert.True(startCalled);
|
|
Assert.True(endCalled);
|
|
|
|
// As Processors can be added anytime after Provider construction, the following validates
|
|
// the following validates that updated processors are processing the legacy activities created from here on.
|
|
TestActivityProcessor testActivityProcessorNew = new TestActivityProcessor();
|
|
|
|
bool startCalledNew = false;
|
|
bool endCalledNew = false;
|
|
|
|
testActivityProcessorNew.StartAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnStart is called, activity's IsAllDataRequested is set to true
|
|
startCalledNew = true;
|
|
};
|
|
|
|
testActivityProcessorNew.EndAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
|
endCalledNew = true;
|
|
};
|
|
|
|
tracerProvider.AddProcessor(testActivityProcessorNew);
|
|
|
|
var sdkProvider = (TracerProviderSdk)tracerProvider;
|
|
|
|
Assert.True(sdkProvider.Processor is CompositeProcessor<Activity>);
|
|
Assert.Equal(tracerProvider, sdkProvider.Processor.ParentProvider);
|
|
Assert.Equal(tracerProvider, testActivityProcessorNew.ParentProvider);
|
|
|
|
Activity activityNew = new Activity(operationNameForLegacyActivity); // Create a new Activity with the same operation name
|
|
activityNew.Start();
|
|
activityNew.Stop();
|
|
|
|
Assert.True(startCalledNew);
|
|
Assert.True(endCalledNew);
|
|
}
|
|
|
|
[Fact]
|
|
public void SdkSamplesLegacyActivityWithAlwaysOnSampler()
|
|
{
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(new AlwaysOnSampler())
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
|
|
Assert.True(activity.IsAllDataRequested);
|
|
Assert.True(activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
|
|
|
|
// Validating ActivityTraceFlags is not enough as it does not get reflected on
|
|
// Id, If the Id is accessed before the sampler runs.
|
|
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/2700
|
|
Assert.EndsWith("-01", activity.Id);
|
|
|
|
activity.Stop();
|
|
}
|
|
|
|
[Fact]
|
|
public void SdkSamplesLegacyActivityWithAlwaysOffSampler()
|
|
{
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(new AlwaysOffSampler())
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
|
|
Assert.False(activity.IsAllDataRequested);
|
|
Assert.False(activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
|
|
|
|
// Validating ActivityTraceFlags is not enough as it does not get reflected on
|
|
// Id, If the Id is accessed before the sampler runs.
|
|
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/2700
|
|
Assert.EndsWith("-00", activity.Id);
|
|
|
|
activity.Stop();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(SamplingDecision.Drop, false, false)]
|
|
[InlineData(SamplingDecision.RecordOnly, true, false)]
|
|
[InlineData(SamplingDecision.RecordAndSample, true, true)]
|
|
public void SdkSamplesLegacyActivityWithCustomSampler(SamplingDecision samplingDecision, bool isAllDataRequested, bool hasRecordedFlag)
|
|
{
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
var sampler = new TestSampler() { SamplingAction = (samplingParameters) => new SamplingResult(samplingDecision) };
|
|
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(sampler)
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
|
|
Assert.Equal(isAllDataRequested, activity.IsAllDataRequested);
|
|
Assert.Equal(hasRecordedFlag, activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
|
|
|
|
// Validating ActivityTraceFlags is not enough as it does not get reflected on
|
|
// Id, If the Id is accessed before the sampler runs.
|
|
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/2700
|
|
Assert.EndsWith(hasRecordedFlag ? "-01" : "-00", activity.Id);
|
|
|
|
activity.Stop();
|
|
}
|
|
|
|
[Fact]
|
|
public void SdkPopulatesSamplingParamsCorrectlyForRootLegacyActivity()
|
|
{
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
var sampler = new TestSampler()
|
|
{
|
|
SamplingAction = (samplingParameters) =>
|
|
{
|
|
Assert.Equal(default, samplingParameters.ParentContext);
|
|
return new SamplingResult(SamplingDecision.RecordAndSample);
|
|
},
|
|
};
|
|
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(sampler)
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
// Start activity without setting parent. i.e it'll have null parent
|
|
// and becomes root activity
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
activity.Stop();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(SamplingDecision.Drop, ActivityTraceFlags.None, false, false)]
|
|
[InlineData(SamplingDecision.Drop, ActivityTraceFlags.Recorded, false, false)]
|
|
[InlineData(SamplingDecision.RecordOnly, ActivityTraceFlags.None, true, false)]
|
|
[InlineData(SamplingDecision.RecordOnly, ActivityTraceFlags.Recorded, true, false)]
|
|
[InlineData(SamplingDecision.RecordAndSample, ActivityTraceFlags.None, true, true)]
|
|
[InlineData(SamplingDecision.RecordAndSample, ActivityTraceFlags.Recorded, true, true)]
|
|
public void SdkSamplesLegacyActivityWithRemoteParentWithCustomSampler(SamplingDecision samplingDecision, ActivityTraceFlags parentTraceFlags, bool expectedIsAllDataRequested, bool hasRecordedFlag)
|
|
{
|
|
var parentTraceId = ActivityTraceId.CreateRandom();
|
|
var parentSpanId = ActivitySpanId.CreateRandom();
|
|
var parentTraceFlag = (parentTraceFlags == ActivityTraceFlags.Recorded) ? "01" : "00";
|
|
string remoteParentId = $"00-{parentTraceId}-{parentSpanId}-{parentTraceFlag}";
|
|
string tracestate = "a=b;c=d";
|
|
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
var sampler = new TestSampler()
|
|
{
|
|
SamplingAction = (samplingParameters) =>
|
|
{
|
|
// Ensure that SDK populates the sampling parameters correctly
|
|
Assert.Equal(parentTraceId, samplingParameters.ParentContext.TraceId);
|
|
Assert.Equal(parentSpanId, samplingParameters.ParentContext.SpanId);
|
|
Assert.Equal(parentTraceFlags, samplingParameters.ParentContext.TraceFlags);
|
|
Assert.Equal(tracestate, samplingParameters.ParentContext.TraceState);
|
|
return new SamplingResult(samplingDecision);
|
|
},
|
|
};
|
|
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(sampler)
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
// Create an activity with remote parent id.
|
|
// The sampling parameters are expected to be that of the
|
|
// parent context i.e the remote parent.
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity).SetParentId(remoteParentId);
|
|
activity.TraceStateString = tracestate;
|
|
|
|
// At this point SetParentId has set the ActivityTraceFlags to that of the parent activity. The activity is now passed to the sampler.
|
|
activity.Start();
|
|
Assert.Equal(expectedIsAllDataRequested, activity.IsAllDataRequested);
|
|
Assert.Equal(hasRecordedFlag, activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
|
|
|
|
// Validating ActivityTraceFlags is not enough as it does not get reflected on
|
|
// Id, If the Id is accessed before the sampler runs.
|
|
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/2700
|
|
Assert.EndsWith(hasRecordedFlag ? "-01" : "-00", activity.Id);
|
|
activity.Stop();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(ActivityTraceFlags.None)]
|
|
[InlineData(ActivityTraceFlags.Recorded)]
|
|
public void SdkSamplesLegacyActivityWithRemoteParentWithAlwaysOnSampler(ActivityTraceFlags parentTraceFlags)
|
|
{
|
|
var parentTraceId = ActivityTraceId.CreateRandom();
|
|
var parentSpanId = ActivitySpanId.CreateRandom();
|
|
var parentTraceFlag = (parentTraceFlags == ActivityTraceFlags.Recorded) ? "01" : "00";
|
|
string remoteParentId = $"00-{parentTraceId}-{parentSpanId}-{parentTraceFlag}";
|
|
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(new AlwaysOnSampler())
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
// Create an activity with remote parent id.
|
|
// The sampling parameters are expected to be that of the
|
|
// parent context i.e the remote parent.
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity).SetParentId(remoteParentId);
|
|
|
|
// At this point SetParentId has set the ActivityTraceFlags to that of the parent activity. The activity is now passed to the sampler.
|
|
activity.Start();
|
|
Assert.True(activity.IsAllDataRequested);
|
|
Assert.True(activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
|
|
|
|
// Validating ActivityTraceFlags is not enough as it does not get reflected on
|
|
// Id, If the Id is accessed before the sampler runs.
|
|
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/2700
|
|
Assert.EndsWith("-01", activity.Id);
|
|
activity.Stop();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(ActivityTraceFlags.None)]
|
|
[InlineData(ActivityTraceFlags.Recorded)]
|
|
public void SdkSamplesLegacyActivityWithRemoteParentWithAlwaysOffSampler(ActivityTraceFlags parentTraceFlags)
|
|
{
|
|
var parentTraceId = ActivityTraceId.CreateRandom();
|
|
var parentSpanId = ActivitySpanId.CreateRandom();
|
|
var parentTraceFlag = (parentTraceFlags == ActivityTraceFlags.Recorded) ? "01" : "00";
|
|
string remoteParentId = $"00-{parentTraceId}-{parentSpanId}-{parentTraceFlag}";
|
|
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(new AlwaysOffSampler())
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
// Create an activity with remote parent id.
|
|
// The sampling parameters are expected to be that of the
|
|
// parent context i.e the remote parent.
|
|
|
|
Activity activity = new Activity(operationNameForLegacyActivity).SetParentId(remoteParentId);
|
|
|
|
// At this point SetParentId has set the ActivityTraceFlags to that of the parent activity. The activity is now passed to the sampler.
|
|
activity.Start();
|
|
Assert.False(activity.IsAllDataRequested);
|
|
Assert.False(activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
|
|
|
|
// Validating ActivityTraceFlags is not enough as it does not get reflected on
|
|
// Id, If the Id is accessed before the sampler runs.
|
|
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/2700
|
|
Assert.EndsWith("-00", activity.Id);
|
|
activity.Stop();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(ActivityTraceFlags.None)]
|
|
[InlineData(ActivityTraceFlags.Recorded)]
|
|
public void SdkPopulatesSamplingParamsCorrectlyForLegacyActivityWithInProcParent(ActivityTraceFlags traceFlags)
|
|
{
|
|
// Create some parent activity.
|
|
string tracestate = "a=b;c=d";
|
|
var activityLocalParent = new Activity("TestParent")
|
|
{
|
|
ActivityTraceFlags = traceFlags,
|
|
TraceStateString = tracestate,
|
|
};
|
|
activityLocalParent.Start();
|
|
|
|
var operationNameForLegacyActivity = "TestOperationName";
|
|
var sampler = new TestSampler()
|
|
{
|
|
SamplingAction = (samplingParameters) =>
|
|
{
|
|
Assert.Equal(activityLocalParent.TraceId, samplingParameters.ParentContext.TraceId);
|
|
Assert.Equal(activityLocalParent.SpanId, samplingParameters.ParentContext.SpanId);
|
|
Assert.Equal(activityLocalParent.ActivityTraceFlags, samplingParameters.ParentContext.TraceFlags);
|
|
Assert.Equal(tracestate, samplingParameters.ParentContext.TraceState);
|
|
return new SamplingResult(SamplingDecision.RecordAndSample);
|
|
},
|
|
};
|
|
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(sampler)
|
|
.AddLegacySource(operationNameForLegacyActivity)
|
|
.Build();
|
|
|
|
// This activity will have a inproc parent.
|
|
// activity.Parent will be equal to the activity created at the beginning of this test.
|
|
// Sampling parameters are expected to be that of the parentContext.
|
|
// i.e of the parent Activity
|
|
Activity activity = new Activity(operationNameForLegacyActivity);
|
|
activity.Start();
|
|
activity.Stop();
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerProvideSdkCreatesAndDiposesInstrumentation()
|
|
{
|
|
TestInstrumentation testInstrumentation = null;
|
|
var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddInstrumentation(() =>
|
|
{
|
|
testInstrumentation = new TestInstrumentation();
|
|
return testInstrumentation;
|
|
})
|
|
.Build();
|
|
|
|
Assert.NotNull(testInstrumentation);
|
|
Assert.False(testInstrumentation.IsDisposed);
|
|
tracerProvider.Dispose();
|
|
Assert.True(testInstrumentation.IsDisposed);
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerProviderSdkBuildsWithDefaultResource()
|
|
{
|
|
var tracerProvider = Sdk.CreateTracerProviderBuilder().Build();
|
|
var resource = tracerProvider.GetResource();
|
|
|
|
Assert.NotNull(resource);
|
|
Assert.NotEqual(Resource.Empty, resource);
|
|
Assert.Single(resource.Attributes);
|
|
Assert.Equal(ResourceSemanticConventions.AttributeServiceName, resource.Attributes.FirstOrDefault().Key);
|
|
Assert.Contains("unknown_service", (string)resource.Attributes.FirstOrDefault().Value);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
public void AddLegacyOperationName_BadArgs(string operationName)
|
|
{
|
|
var builder = Sdk.CreateTracerProviderBuilder();
|
|
Assert.Throws<ArgumentException>(() => builder.AddLegacySource(operationName));
|
|
}
|
|
|
|
[Fact]
|
|
public void AddLegacyOperationNameAddsActivityListenerForEmptyActivitySource()
|
|
{
|
|
var emptyActivitySource = new ActivitySource(string.Empty);
|
|
var builder = Sdk.CreateTracerProviderBuilder();
|
|
builder.AddLegacySource("TestOperationName");
|
|
|
|
Assert.False(emptyActivitySource.HasListeners());
|
|
using var provider = builder.Build();
|
|
Assert.True(emptyActivitySource.HasListeners());
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(true)]
|
|
[InlineData(false)]
|
|
public void TracerProviderSdkBuildsWithSDKResource(bool useConfigure)
|
|
{
|
|
var tracerProvider = useConfigure ?
|
|
Sdk.CreateTracerProviderBuilder().SetResourceBuilder(
|
|
ResourceBuilder.CreateDefault().AddTelemetrySdk()).Build() :
|
|
Sdk.CreateTracerProviderBuilder().ConfigureResource(r => r.AddTelemetrySdk()).Build();
|
|
var resource = tracerProvider.GetResource();
|
|
var attributes = resource.Attributes;
|
|
|
|
Assert.NotNull(resource);
|
|
Assert.NotEqual(Resource.Empty, resource);
|
|
Assert.Contains(new KeyValuePair<string, object>("telemetry.sdk.name", "opentelemetry"), attributes);
|
|
Assert.Contains(new KeyValuePair<string, object>("telemetry.sdk.language", "dotnet"), attributes);
|
|
var versionAttribute = attributes.Where(pair => pair.Key.Equals("telemetry.sdk.version"));
|
|
Assert.Single(versionAttribute);
|
|
}
|
|
|
|
[Fact]
|
|
public void TracerProviderSdkFlushesProcessorForcibly()
|
|
{
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.AddProcessor(testActivityProcessor)
|
|
.Build();
|
|
|
|
var isFlushed = tracerProvider.ForceFlush();
|
|
|
|
Assert.True(isFlushed);
|
|
Assert.True(testActivityProcessor.ForceFlushCalled);
|
|
}
|
|
|
|
[Fact]
|
|
public void SdkSamplesAndProcessesLegacySourceWhenAddLegacySourceIsCalledWithWildcardValue()
|
|
{
|
|
var sampledActivities = new List<string>();
|
|
var sampler = new TestSampler
|
|
{
|
|
SamplingAction =
|
|
(samplingParameters) =>
|
|
{
|
|
sampledActivities.Add(samplingParameters.Name);
|
|
return new SamplingResult(SamplingDecision.RecordAndSample);
|
|
},
|
|
};
|
|
|
|
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
|
|
|
var onStartProcessedActivities = new List<string>();
|
|
var onStopProcessedActivities = new List<string>();
|
|
testActivityProcessor.StartAction =
|
|
(a) =>
|
|
{
|
|
Assert.Contains(a.OperationName, sampledActivities);
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnStart is called, activity's IsAllDataRequested is set to true
|
|
onStartProcessedActivities.Add(a.OperationName);
|
|
};
|
|
|
|
testActivityProcessor.EndAction =
|
|
(a) =>
|
|
{
|
|
Assert.False(Sdk.SuppressInstrumentation);
|
|
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
|
onStopProcessedActivities.Add(a.OperationName);
|
|
};
|
|
|
|
var legacySourceNamespaces = new[] { "LegacyNamespace.*", "Namespace.*.Operation" };
|
|
using var activitySource = new ActivitySource(ActivitySourceName);
|
|
|
|
// AddLegacyOperationName chained to TracerProviderBuilder
|
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
|
.SetSampler(sampler)
|
|
.AddProcessor(testActivityProcessor)
|
|
.AddLegacySource(legacySourceNamespaces[0])
|
|
.AddLegacySource(legacySourceNamespaces[1])
|
|
.AddSource(ActivitySourceName)
|
|
.Build();
|
|
|
|
foreach (var ns in legacySourceNamespaces)
|
|
{
|
|
var startOpName = ns.Replace("*", "Start");
|
|
Activity startOperation = new Activity(startOpName);
|
|
startOperation.Start();
|
|
startOperation.Stop();
|
|
|
|
Assert.Contains(startOpName, onStartProcessedActivities); // Processor.OnStart is called since we added a legacy OperationName
|
|
Assert.Contains(startOpName, onStopProcessedActivities); // Processor.OnEnd is called since we added a legacy OperationName
|
|
|
|
var stopOpName = ns.Replace("*", "Stop");
|
|
Activity stopOperation = new Activity(stopOpName);
|
|
stopOperation.Start();
|
|
stopOperation.Stop();
|
|
|
|
Assert.Contains(stopOpName, onStartProcessedActivities); // Processor.OnStart is called since we added a legacy OperationName
|
|
Assert.Contains(stopOpName, onStopProcessedActivities); // Processor.OnEnd is called since we added a legacy OperationName
|
|
}
|
|
|
|
// Ensure we can still process "normal" activities when in legacy wildcard mode.
|
|
Activity nonLegacyActivity = activitySource.StartActivity("TestActivity");
|
|
nonLegacyActivity.Start();
|
|
nonLegacyActivity.Stop();
|
|
|
|
Assert.Contains(nonLegacyActivity.OperationName, onStartProcessedActivities); // Processor.OnStart is called since we added a legacy OperationName
|
|
Assert.Contains(nonLegacyActivity.OperationName, onStopProcessedActivities); // Processor.OnEnd is called since we added a legacy OperationName
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private class TestInstrumentation : IDisposable
|
|
{
|
|
public bool IsDisposed;
|
|
|
|
public TestInstrumentation()
|
|
{
|
|
this.IsDisposed = false;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
this.IsDisposed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|