Implement Activity.GetTagItem (#47443)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Wed, 27 Jan 2021 01:14:50 +0000 (17:14 -0800)
committerGitHub <noreply@github.com>
Wed, 27 Jan 2021 01:14:50 +0000 (17:14 -0800)
* Implement Activity.GetTagItem

* Address the feedback

src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs

index 7ec1423..aea6c7d 100644 (file)
@@ -59,6 +59,7 @@ namespace System.Diagnostics
         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; }
index 476af07..06c2e61 100644 (file)
@@ -329,11 +329,19 @@ namespace System.Diagnostics
             return null;
         }
 
+        /// <summary>
+        /// Returns the value of the Activity tag mapped to the input key/>.
+        /// Returns null if that key does not exist.
+        /// </summary>
+        /// <param name="key">The tag key string.</param>
+        /// <returns>The tag value mapped to the input key.</returns>
+        public object? GetTagItem(string key) => _tags?.Get(key) ?? null;
+
         /* Constructors  Builder methods */
 
         /// <summary>
         /// Note that Activity has a 'builder' pattern, where you call the constructor, a number of 'Set*' and 'Add*' APIs and then
-        /// call <see cref="Start"/> to build the activity.  You MUST call <see cref="Start"/> before using it.
+        /// call <see cref="Start"/> to build the activity. You MUST call <see cref="Start"/> before using it.
         /// </summary>
         /// <param name="operationName">Operation's name <see cref="OperationName"/></param>
         public Activity(string operationName)
@@ -1484,6 +1492,23 @@ namespace System.Diagnostics
                 }
             }
 
+            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<KeyValuePair<string, object?>>? current = _first;
+                while (current != null)
+                {
+                    if (current.Value.Key == key)
+                    {
+                        return current.Value.Value;
+                    }
+
+                    current = current.Next;
+                }
+
+                return null;
+            }
+
             public void Remove(string key)
             {
 
index 531c443..4309668 100644 (file)
@@ -140,12 +140,12 @@ namespace System.Diagnostics.Tests
             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 @@ namespace System.Diagnostics.Tests
             // 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 @@ namespace System.Diagnostics.Tests
             }
         }
 
+        [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)]