Skip to content

Commit 3393b4d

Browse files
authored
Add ActivitySource Tags (#102678)
* Add ActivitySource Tags * Fix failing test
1 parent 75c670e commit 3393b4d

File tree

6 files changed

+48
-4
lines changed

6 files changed

+48
-4
lines changed

src/libraries/Common/src/System/Diagnostics/DiagnosticsHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal static class DiagnosticsHelper
1919
/// we avoid the allocation of a new array by using the second collection as is and not converting it to an array. the reason
2020
/// is we call this every time we try to create a meter or instrument and we don't want to allocate a new array every time.
2121
/// </remarks>
22-
internal static bool CompareTags(List<KeyValuePair<string, object?>>? sortedTags, IEnumerable<KeyValuePair<string, object?>>? tags2)
22+
internal static bool CompareTags(IList<KeyValuePair<string, object?>>? sortedTags, IEnumerable<KeyValuePair<string, object?>>? tags2)
2323
{
2424
if (sortedTags == tags2)
2525
{

src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Collections.ObjectModel;
67
using System.Diagnostics;
78
using System.Diagnostics.Metrics;
89

@@ -40,7 +41,7 @@ public Meter Create(MeterOptions options)
4041
{
4142
foreach (Meter meter in meterList)
4243
{
43-
if (meter.Version == options.Version && DiagnosticsHelper.CompareTags(meter.Tags as List<KeyValuePair<string, object?>>, options.Tags))
44+
if (meter.Version == options.Version && DiagnosticsHelper.CompareTags(meter.Tags as IList<KeyValuePair<string, object?>>, options.Tags))
4445
{
4546
return meter;
4647
}

src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,13 @@ public void CopyTo(System.Span<byte> destination) { }
150150
}
151151
public sealed class ActivitySource : IDisposable
152152
{
153+
public ActivitySource(string name) { throw null; }
154+
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
153155
public ActivitySource(string name, string? version = "") { throw null; }
156+
public ActivitySource(string name, string? version = "", System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object?>>? tags = default) { throw null; }
154157
public string Name { get { throw null; } }
155158
public string? Version { get { throw null; } }
159+
public System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object?>>? Tags { get { throw null; } }
156160
public bool HasListeners() { throw null; }
157161
public System.Diagnostics.Activity? CreateActivity(string name, System.Diagnostics.ActivityKind kind) { throw null; }
158162
public System.Diagnostics.Activity? CreateActivity(string name, System.Diagnostics.ActivityKind kind, System.Diagnostics.ActivityContext parentContext, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object?>>? tags = null, System.Collections.Generic.IEnumerable<System.Diagnostics.ActivityLink>? links = null, System.Diagnostics.ActivityIdFormat idFormat = System.Diagnostics.ActivityIdFormat.Unknown) { throw null; }

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.ComponentModel;
56
using System.Runtime.CompilerServices;
67
using System.Threading;
78

@@ -13,16 +14,40 @@ public sealed class ActivitySource : IDisposable
1314
private static readonly SynchronizedList<ActivityListener> s_allListeners = new SynchronizedList<ActivityListener>();
1415
private SynchronizedList<ActivityListener>? _listeners;
1516

17+
/// <summary>
18+
/// Construct an ActivitySource object with the input name
19+
/// </summary>
20+
/// <param name="name">The name of the ActivitySource object</param>
21+
public ActivitySource(string name) : this(name, version: "", tags: null) {}
22+
1623
/// <summary>
1724
/// Construct an ActivitySource object with the input name
1825
/// </summary>
1926
/// <param name="name">The name of the ActivitySource object</param>
2027
/// <param name="version">The version of the component publishing the tracing info.</param>
21-
public ActivitySource(string name, string? version = "")
28+
[EditorBrowsable(EditorBrowsableState.Never)]
29+
public ActivitySource(string name, string? version = "") : this(name, version, tags: null) {}
30+
31+
/// <summary>
32+
/// Construct an ActivitySource object with the input name
33+
/// </summary>
34+
/// <param name="name">The name of the ActivitySource object</param>
35+
/// <param name="version">The version of the component publishing the tracing info.</param>
36+
/// <param name="tags">The optional ActivitySource tags.</param>
37+
public ActivitySource(string name, string? version = "", IEnumerable<KeyValuePair<string, object?>>? tags = default)
2238
{
2339
Name = name ?? throw new ArgumentNullException(nameof(name));
2440
Version = version;
2541

42+
// Sorting the tags to make sure the tags are always in the same order.
43+
// Sorting can help in comparing the tags used for any scenario.
44+
if (tags is not null)
45+
{
46+
var tagList = new List<KeyValuePair<string, object?>>(tags);
47+
tagList.Sort((left, right) => string.Compare(left.Key, right.Key, StringComparison.Ordinal));
48+
Tags = tagList.AsReadOnly();
49+
}
50+
2651
s_activeSources.Add(this);
2752

2853
if (s_allListeners.Count > 0)
@@ -54,6 +79,11 @@ public ActivitySource(string name, string? version = "")
5479
/// </summary>
5580
public string? Version { get; }
5681

82+
/// <summary>
83+
/// Returns the tags associated with the ActivitySource.
84+
/// </summary>
85+
public IEnumerable<KeyValuePair<string, object?>>? Tags { get; }
86+
5787
/// <summary>
5888
/// Check if there is any listeners for this ActivitySource.
5989
/// This property can be helpful to tell if there is no listener, then no need to create Activity object

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private void Initialize(string name, string? version, IEnumerable<KeyValuePair<s
7979
{
8080
var tagList = new List<KeyValuePair<string, object?>>(tags);
8181
tagList.Sort((left, right) => string.Compare(left.Key, right.Key, StringComparison.Ordinal));
82-
Tags = tagList;
82+
Tags = tagList.AsReadOnly();
8383
}
8484
Scope = scope;
8585

src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,20 @@ public void TestConstruction()
2323
Assert.Equal("Source1", as1.Name);
2424
Assert.Equal(String.Empty, as1.Version);
2525
Assert.False(as1.HasListeners());
26+
Assert.Null(as1.Tags);
2627

2728
using ActivitySource as2 = new ActivitySource("Source2", "1.1.1.2");
2829
Assert.Equal("Source2", as2.Name);
2930
Assert.Equal("1.1.1.2", as2.Version);
3031
Assert.False(as2.HasListeners());
32+
Assert.Null(as2.Tags);
33+
34+
using ActivitySource as3 = new ActivitySource("Source3", "1.1.1.3", new TagList { { "key3", "value3" }, { "key2", "value2" }, { "key1", "value1" } });
35+
Assert.Equal("Source3", as3.Name);
36+
Assert.Equal("1.1.1.3", as3.Version);
37+
Assert.False(as3.HasListeners());
38+
// Ensure the tags are sorted by key.
39+
Assert.Equal(new TagList { { "key1", "value1" }, { "key2", "value2" }, { "key3", "value3" } }, as3.Tags);
3140
}).Dispose();
3241
}
3342

0 commit comments

Comments
 (0)