Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1197,7 +1197,10 @@ internal static Activity Create(ActivitySource source, string name, ActivityKind
activity.IdFormat = idFormat;
activity._traceState = traceState;

if (links != null)
// Links and creation tags are unnecessary for activities sampled as PropagationData, so skip copying them.
bool copyData = request is ActivitySamplingResult.AllData or ActivitySamplingResult.AllDataAndRecorded;
Comment thread
tarekgh marked this conversation as resolved.

if (copyData && links != null)
{
using (IEnumerator<ActivityLink> enumerator = links.GetEnumerator())
{
Expand All @@ -1208,7 +1211,7 @@ internal static Activity Create(ActivitySource source, string name, ActivityKind
}
}

if (tags != null)
if (copyData && tags != null)
{
using (IEnumerator<KeyValuePair<string, object?>> enumerator = tags.GetEnumerator())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,16 @@ public bool HasListeners()
/// <param name="name">The operation name of the Activity.</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <param name="parentContext">The parent <see cref="ActivityContext"/> object to initialize the created Activity object with.</param>
/// <param name="tags">The optional tags list to initialize the created Activity object with.</param>
/// <param name="links">The optional <see cref="ActivityLink"/> list to initialize the created Activity object with.</param>
/// <param name="tags">
/// The optional tags list to initialize the created Activity object with. The tags are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="links">
/// The optional <see cref="ActivityLink"/> list to initialize the created Activity object with. The links are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="idFormat">The default Id format to use.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
/// <remarks>
Expand All @@ -169,8 +177,16 @@ public bool HasListeners()
/// <param name="name">The operation name of the Activity.</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <param name="parentId">The parent Id to initialize the created Activity object with.</param>
/// <param name="tags">The optional tags list to initialize the created Activity object with.</param>
/// <param name="links">The optional <see cref="ActivityLink"/> list to initialize the created Activity object with.</param>
/// <param name="tags">
/// The optional tags list to initialize the created Activity object with. The tags are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="links">
/// The optional <see cref="ActivityLink"/> list to initialize the created Activity object with. The links are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="idFormat">The default Id format to use.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
/// <remarks>
Expand All @@ -194,8 +210,16 @@ public bool HasListeners()
/// <param name="name">The operation name of the Activity.</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <param name="parentContext">The parent <see cref="ActivityContext"/> object to initialize the created Activity object with.</param>
/// <param name="tags">The optional tags list to initialize the created Activity object with.</param>
/// <param name="links">The optional <see cref="ActivityLink"/> list to initialize the created Activity object with.</param>
/// <param name="tags">
/// The optional tags list to initialize the created Activity object with. The tags are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="links">
/// The optional <see cref="ActivityLink"/> list to initialize the created Activity object with. The links are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="startTime">The optional start timestamp to set on the created Activity object.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
public Activity? StartActivity(string name, ActivityKind kind, ActivityContext parentContext, IEnumerable<KeyValuePair<string, object?>>? tags = null, IEnumerable<ActivityLink>? links = null, DateTimeOffset startTime = default)
Expand All @@ -207,8 +231,16 @@ public bool HasListeners()
/// <param name="name">The operation name of the Activity.</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <param name="parentId">The parent Id to initialize the created Activity object with.</param>
/// <param name="tags">The optional tags list to initialize the created Activity object with.</param>
/// <param name="links">The optional <see cref="ActivityLink"/> list to initialize the created Activity object with.</param>
/// <param name="tags">
/// The optional tags list to initialize the created Activity object with. The tags are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="links">
/// The optional <see cref="ActivityLink"/> list to initialize the created Activity object with. The links are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="startTime">The optional start timestamp to set on the created Activity object.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
public Activity? StartActivity(string name, ActivityKind kind, string? parentId, IEnumerable<KeyValuePair<string, object?>>? tags = null, IEnumerable<ActivityLink>? links = null, DateTimeOffset startTime = default)
Expand All @@ -219,8 +251,16 @@ public bool HasListeners()
/// </summary>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <param name="parentContext">The parent <see cref="ActivityContext"/> object to initialize the created Activity object with.</param>
/// <param name="tags">The optional tags list to initialize the created Activity object with.</param>
/// <param name="links">The optional <see cref="ActivityLink"/> list to initialize the created Activity object with.</param>
/// <param name="tags">
/// The optional tags list to initialize the created Activity object with. The tags are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="links">
/// The optional <see cref="ActivityLink"/> list to initialize the created Activity object with. The links are made available to sampling callbacks,
/// but are only copied to the created Activity if the combined sampling result is <see cref="ActivitySamplingResult.AllData"/>
/// or <see cref="ActivitySamplingResult.AllDataAndRecorded"/>.
/// </param>
/// <param name="startTime">The optional start timestamp to set on the created Activity object.</param>
/// <param name="name">The operation name of the Activity.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,194 @@ public void PropagationDataSamplingTest()
}).Dispose();
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void PropagationDataSamplingDoesNotCopyTagsAndLinksTest()
{
RemoteExecutor.Invoke(() => {
Activity.ForceDefaultIdFormat = true;
Activity.DefaultIdFormat = ActivityIdFormat.W3C;

using ActivitySource aSource = new ActivitySource("PropagationDataTagsAndLinksTest");

ActivitySamplingResult result = ActivitySamplingResult.PropagationData;
int sampledTags = 0;
int sampledLinks = 0;

using ActivityListener listener = new ActivityListener
{
ShouldListenTo = (activitySource) => ReferenceEquals(activitySource, aSource),
Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
{
sampledTags = activityOptions.Tags.Count();
sampledLinks = activityOptions.Links.Count();
return result;
}
};

ActivitySource.AddActivityListener(listener);

KeyValuePair<string, object>[] tags = [new("tag1", "value1"), new("tag2", "value2")];
ActivityLink[] links = [new ActivityLink(new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None))];

using (Activity a = aSource.StartActivity("a", ActivityKind.Server, default(ActivityContext), tags, links))
{
Assert.NotNull(a);
Assert.False(a.IsAllDataRequested);
Assert.Empty(a.TagObjects);
Assert.Empty(a.Links);
Assert.Equal(2, sampledTags);
Assert.Equal(1, sampledLinks);
}

result = ActivitySamplingResult.AllData;

using (Activity a = aSource.StartActivity("a", ActivityKind.Server, default(ActivityContext), tags, links))
{
Assert.NotNull(a);
Assert.True(a.IsAllDataRequested);
Assert.Equal(2, a.TagObjects.Count());
Assert.Single(a.Links);
}
}).Dispose();
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void PropagationDataSamplingWithParentIdDoesNotCopyTagsAndLinksTest()
{
RemoteExecutor.Invoke(() => {
Activity.ForceDefaultIdFormat = true;
Activity.DefaultIdFormat = ActivityIdFormat.W3C;

using ActivitySource aSource = new ActivitySource("PropagationDataParentIdTest");

ActivitySamplingResult result = ActivitySamplingResult.PropagationData;
int sampledTags = 0;
int sampledLinks = 0;

using ActivityListener listener = new ActivityListener
{
ShouldListenTo = (activitySource) => ReferenceEquals(activitySource, aSource),
SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) =>
{
sampledTags = activityOptions.Tags.Count();
sampledLinks = activityOptions.Links.Count();
return result;
}
};

ActivitySource.AddActivityListener(listener);

string parentId = "00-0123456789abcdef0123456789abcdef-0123456789abcdef-01";
KeyValuePair<string, object>[] tags = [new("tag1", "value1")];
ActivityLink[] links = [new ActivityLink(new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None))];

using (Activity a = aSource.StartActivity("a", ActivityKind.Server, parentId, tags, links))
{
Assert.NotNull(a);
Assert.False(a.IsAllDataRequested);
Assert.Equal(parentId, a.ParentId);
Assert.Empty(a.TagObjects);
Assert.Empty(a.Links);
Assert.Equal(1, sampledTags);
Assert.Equal(1, sampledLinks);
}

result = ActivitySamplingResult.AllData;

using (Activity a = aSource.StartActivity("a", ActivityKind.Server, parentId, tags, links))
{
Assert.NotNull(a);
Assert.True(a.IsAllDataRequested);
Assert.Single(a.TagObjects);
Assert.Single(a.Links);
}
}).Dispose();
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void PropagationDataSamplingCopiesSamplingTagsButNotCreationTagsTest()
{
RemoteExecutor.Invoke(() => {
using ActivitySource aSource = new ActivitySource("PropagationDataSamplingTagsTest");

ActivitySamplingResult result = ActivitySamplingResult.PropagationData;

using ActivityListener listener = new ActivityListener
{
ShouldListenTo = (activitySource) => ReferenceEquals(activitySource, aSource),
Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
{
activityOptions.SamplingTags.Add("sampler.tag", "value");
return result;
}
};

ActivitySource.AddActivityListener(listener);

KeyValuePair<string, object>[] tags = [new("creation.tag", "value")];

using (Activity a = aSource.StartActivity("a", ActivityKind.Server, default(ActivityContext), tags))
{
Assert.NotNull(a);
Assert.False(a.IsAllDataRequested);
Assert.Equal([new KeyValuePair<string, object>("sampler.tag", "value")], a.TagObjects);
}

result = ActivitySamplingResult.AllData;

using (Activity a = aSource.StartActivity("a", ActivityKind.Server, default(ActivityContext), tags))
{
Assert.NotNull(a);
Assert.True(a.IsAllDataRequested);
Assert.Contains(a.TagObjects, t => t.Key == "creation.tag" && (string)t.Value == "value");
Assert.Contains(a.TagObjects, t => t.Key == "sampler.tag" && (string)t.Value == "value");
}
}).Dispose();
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void PropagationDataSamplingWithMultipleListenersCopiesTagsAndLinksIfAnyRequestsDataTest()
{
RemoteExecutor.Invoke(() => {
using ActivitySource aSource = new ActivitySource("PropagationDataMultipleListenersTest");

using ActivityListener propagationListener = new ActivityListener
{
ShouldListenTo = (activitySource) => ReferenceEquals(activitySource, aSource),
Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.PropagationData
};

ActivitySource.AddActivityListener(propagationListener);

KeyValuePair<string, object>[] tags = [new("tag1", "value1")];
ActivityLink[] links = [new ActivityLink(new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None))];

using (Activity a = aSource.StartActivity("a", ActivityKind.Server, default(ActivityContext), tags, links))
{
Assert.NotNull(a);
Assert.False(a.IsAllDataRequested);
Assert.Empty(a.TagObjects);
Assert.Empty(a.Links);
}

using ActivityListener allDataListener = new ActivityListener
{
ShouldListenTo = (activitySource) => ReferenceEquals(activitySource, aSource),
Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.AllData
};

ActivitySource.AddActivityListener(allDataListener);

using (Activity a = aSource.StartActivity("a", ActivityKind.Server, default(ActivityContext), tags, links))
{
Assert.NotNull(a);
Assert.True(a.IsAllDataRequested);
Assert.Single(a.TagObjects);
Assert.Single(a.Links);
}
}).Dispose();
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestExpectedListenersReturnValues()
{
Expand Down
Loading