From fa34025c5821b6d1df9a4b5b1a7f7388df1c1db7 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Fri, 24 May 2024 16:44:38 -0700 Subject: [PATCH 1/2] Add ActivitySource Tags --- ...em.Diagnostics.DiagnosticSourceActivity.cs | 4 +++ .../src/System/Diagnostics/ActivitySource.cs | 32 ++++++++++++++++++- .../src/System/Diagnostics/Metrics/Meter.cs | 2 +- .../tests/ActivitySourceTests.cs | 9 ++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs index 0e24044c733a7..b047c8f80e122 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -150,9 +150,13 @@ public void CopyTo(System.Span destination) { } } public sealed class ActivitySource : IDisposable { + public ActivitySource(string name) { throw null; } + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public ActivitySource(string name, string? version = "") { throw null; } + public ActivitySource(string name, string? version = "", System.Collections.Generic.IEnumerable>? tags = default) { throw null; } public string Name { get { throw null; } } public string? Version { get { throw null; } } + public System.Collections.Generic.IEnumerable>? Tags { get { throw null; } } public bool HasListeners() { throw null; } public System.Diagnostics.Activity? CreateActivity(string name, System.Diagnostics.ActivityKind kind) { throw null; } public System.Diagnostics.Activity? CreateActivity(string name, System.Diagnostics.ActivityKind kind, System.Diagnostics.ActivityContext parentContext, System.Collections.Generic.IEnumerable>? tags = null, System.Collections.Generic.IEnumerable? links = null, System.Diagnostics.ActivityIdFormat idFormat = System.Diagnostics.ActivityIdFormat.Unknown) { throw null; } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs index e826880d89a94..40bf85faa7e80 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.CompilerServices; using System.Threading; @@ -13,16 +14,40 @@ public sealed class ActivitySource : IDisposable private static readonly SynchronizedList s_allListeners = new SynchronizedList(); private SynchronizedList? _listeners; + /// + /// Construct an ActivitySource object with the input name + /// + /// The name of the ActivitySource object + public ActivitySource(string name) : this(name, version: "", tags: null) {} + /// /// Construct an ActivitySource object with the input name /// /// The name of the ActivitySource object /// The version of the component publishing the tracing info. - public ActivitySource(string name, string? version = "") + [EditorBrowsable(EditorBrowsableState.Never)] + public ActivitySource(string name, string? version = "") : this(name, version, tags: null) {} + + /// + /// Construct an ActivitySource object with the input name + /// + /// The name of the ActivitySource object + /// The version of the component publishing the tracing info. + /// The optional ActivitySource tags. + public ActivitySource(string name, string? version = "", IEnumerable>? tags = default) { Name = name ?? throw new ArgumentNullException(nameof(name)); Version = version; + // Sorting the tags to make sure the tags are always in the same order. + // Sorting can help in comparing the tags used for any scenario. + if (tags is not null) + { + var tagList = new List>(tags); + tagList.Sort((left, right) => string.Compare(left.Key, right.Key, StringComparison.Ordinal)); + Tags = tagList.AsReadOnly(); + } + s_activeSources.Add(this); if (s_allListeners.Count > 0) @@ -54,6 +79,11 @@ public ActivitySource(string name, string? version = "") /// public string? Version { get; } + /// + /// Returns the tags associated with the ActivitySource. + /// + public IEnumerable>? Tags { get; } + /// /// Check if there is any listeners for this ActivitySource. /// This property can be helpful to tell if there is no listener, then no need to create Activity object diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs index 856725680857c..b91631822585b 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs @@ -79,7 +79,7 @@ private void Initialize(string name, string? version, IEnumerable>(tags); tagList.Sort((left, right) => string.Compare(left.Key, right.Key, StringComparison.Ordinal)); - Tags = tagList; + Tags = tagList.AsReadOnly(); } Scope = scope; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs index 98903b9df6938..86227dc2e7427 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs @@ -23,11 +23,20 @@ public void TestConstruction() Assert.Equal("Source1", as1.Name); Assert.Equal(String.Empty, as1.Version); Assert.False(as1.HasListeners()); + Assert.Null(as1.Tags); using ActivitySource as2 = new ActivitySource("Source2", "1.1.1.2"); Assert.Equal("Source2", as2.Name); Assert.Equal("1.1.1.2", as2.Version); Assert.False(as2.HasListeners()); + Assert.Null(as2.Tags); + + using ActivitySource as3 = new ActivitySource("Source3", "1.1.1.3", new TagList { { "key3", "value3" }, { "key2", "value2" }, { "key1", "value1" } }); + Assert.Equal("Source3", as3.Name); + Assert.Equal("1.1.1.3", as3.Version); + Assert.False(as3.HasListeners()); + // Ensure the tags are sorted by key. + Assert.Equal(new TagList { { "key1", "value1" }, { "key2", "value2" }, { "key3", "value3" } }, as3.Tags); }).Dispose(); } From 1efc27e2d78a52364a37fa7ae90adca70d3f0aef Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Sat, 25 May 2024 15:53:11 -0700 Subject: [PATCH 2/2] Fix failing test --- .../Common/src/System/Diagnostics/DiagnosticsHelper.cs | 2 +- .../src/Metrics/DefaultMeterFactory.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/src/System/Diagnostics/DiagnosticsHelper.cs b/src/libraries/Common/src/System/Diagnostics/DiagnosticsHelper.cs index ce7f345b0ea9c..92f75742af350 100644 --- a/src/libraries/Common/src/System/Diagnostics/DiagnosticsHelper.cs +++ b/src/libraries/Common/src/System/Diagnostics/DiagnosticsHelper.cs @@ -19,7 +19,7 @@ internal static class DiagnosticsHelper /// we avoid the allocation of a new array by using the second collection as is and not converting it to an array. the reason /// 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. /// - internal static bool CompareTags(List>? sortedTags, IEnumerable>? tags2) + internal static bool CompareTags(IList>? sortedTags, IEnumerable>? tags2) { if (sortedTags == tags2) { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs index dd1be2d1512d3..ca1ca5f7802e4 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.Metrics; @@ -40,7 +41,7 @@ public Meter Create(MeterOptions options) { foreach (Meter meter in meterList) { - if (meter.Version == options.Version && DiagnosticsHelper.CompareTags(meter.Tags as List>, options.Tags)) + if (meter.Version == options.Version && DiagnosticsHelper.CompareTags(meter.Tags as IList>, options.Tags)) { return meter; }