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 7ec1423a05a41a..aea6c7d7ef3d54 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -59,6 +59,7 @@ public string? Id public System.Diagnostics.Activity SetTag(string key, object? value) { throw null; } public System.Diagnostics.Activity SetBaggage(string key, string? value) { throw null; } public string? GetBaggageItem(string key) { throw null; } + public object? GetTagItem(string key) { throw null; } public System.Diagnostics.Activity SetEndTime(System.DateTime endTimeUtc) { throw null; } public System.Diagnostics.Activity SetIdFormat(System.Diagnostics.ActivityIdFormat format) { throw null; } public System.Diagnostics.Activity SetParentId(System.Diagnostics.ActivityTraceId traceId, System.Diagnostics.ActivitySpanId spanId, System.Diagnostics.ActivityTraceFlags activityTraceFlags = System.Diagnostics.ActivityTraceFlags.None) { throw null; } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs index 476af07d0c162b..06c2e61b37175b 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs @@ -329,11 +329,19 @@ public IEnumerable Links return null; } + /// + /// Returns the value of the Activity tag mapped to the input key/>. + /// Returns null if that key does not exist. + /// + /// The tag key string. + /// The tag value mapped to the input key. + public object? GetTagItem(string key) => _tags?.Get(key) ?? null; + /* Constructors Builder methods */ /// /// Note that Activity has a 'builder' pattern, where you call the constructor, a number of 'Set*' and 'Add*' APIs and then - /// call to build the activity. You MUST call before using it. + /// call to build the activity. You MUST call before using it. /// /// Operation's name public Activity(string operationName) @@ -1484,6 +1492,23 @@ public void Add(KeyValuePair value) } } + public object? Get(string key) + { + // We don't take the lock here so it is possible the Add/Remove operations mutate the list during the Get operation. + LinkedListNode>? current = _first; + while (current != null) + { + if (current.Value.Key == key) + { + return current.Value.Value; + } + + current = current.Next; + } + + return null; + } + public void Remove(string key) { diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs index 531c443de567c1..430966818bad3e 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs @@ -140,12 +140,12 @@ public void TestSetBaggage() Assert.Equal("5", a.GetBaggageItem("5")); // Check not added item - Assert.Null(a.GetBaggageItem("6")); + Assert.Null(a.GetBaggageItem("6")); // Adding none existing key with null value is no-op a.SetBaggage("6", null); Assert.Equal(5, a.Baggage.Count()); - Assert.Null(a.GetBaggageItem("6")); + Assert.Null(a.GetBaggageItem("6")); // Check updated item a.SetBaggage("5", "5.1"); @@ -166,7 +166,7 @@ public void TestSetBaggage() // Now Remove second item a.SetBaggage("5", null); Assert.Equal(4, a.Baggage.Count()); - Assert.Null(a.GetBaggageItem("5")); + Assert.Null(a.GetBaggageItem("5")); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] @@ -1606,6 +1606,57 @@ public void TestTagObjects() } } + [Fact] + public void TestGetTagItem() + { + Activity a = new Activity("GetTagItem"); + + // Test empty tags list + Assert.Equal(0, a.TagObjects.Count()); + Assert.Null(a.GetTagItem("tag1")); + + // Test adding first tag + a.AddTag("tag1", "value1"); + Assert.Equal(1, a.TagObjects.Count()); + Assert.Equal("value1", a.GetTagItem("tag1")); + Assert.Null(a.GetTagItem("tag2")); + + // Test adding one more key + a.AddTag("tag2", "value2"); + Assert.Equal(2, a.TagObjects.Count()); + Assert.Equal("value1", a.GetTagItem("tag1")); + Assert.Equal("value2", a.GetTagItem("tag2")); + + // Test adding duplicate key + a.AddTag("tag1", "value1-d"); + Assert.Equal(3, a.TagObjects.Count()); + Assert.Equal("value1", a.GetTagItem("tag1")); + Assert.Equal("value2", a.GetTagItem("tag2")); + + // Test setting the key (overwrite the value) + a.SetTag("tag1", "value1-O"); + Assert.Equal(3, a.TagObjects.Count()); + Assert.Equal("value1-O", a.GetTagItem("tag1")); + Assert.Equal("value2", a.GetTagItem("tag2")); + + // Test removing the key + a.SetTag("tag1", null); + Assert.Equal(2, a.TagObjects.Count()); + Assert.Equal("value1-d", a.GetTagItem("tag1")); + Assert.Equal("value2", a.GetTagItem("tag2")); + + a.SetTag("tag1", null); + Assert.Equal(1, a.TagObjects.Count()); + Assert.Null(a.GetTagItem("tag1")); + Assert.Equal("value2", a.GetTagItem("tag2")); + + a.SetTag("tag2", null); + Assert.Equal(0, a.TagObjects.Count()); + Assert.Null(a.GetTagItem("tag1")); + Assert.Null(a.GetTagItem("tag2")); + } + + [Theory] [InlineData("key1", null, true, 1)] [InlineData("key2", null, false, 0)]