System.Diagnostics Tracing APIs additions (#40534)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Mon, 10 Aug 2020 16:27:24 +0000 (09:27 -0700)
committerGitHub <noreply@github.com>
Mon, 10 Aug 2020 16:27:24 +0000 (09:27 -0700)
src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs
src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityCreationOptions.cs
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityListener.cs
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySamplingResult.cs [moved from src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityDataRequest.cs with 97% similarity]
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs
src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs

index 8e0f944..962b663 100644 (file)
@@ -179,7 +179,7 @@ namespace System.Diagnostics
         public System.Diagnostics.Activity StartActivity(System.Diagnostics.Activity activity, object? args) { throw null; }
         public void StopActivity(System.Diagnostics.Activity activity, object? args) { }
     }
-    public enum ActivityDataRequest
+    public enum ActivitySamplingResult
     {
         None,
         PropagationData,
@@ -237,17 +237,18 @@ namespace System.Diagnostics
         public T Parent  { get { throw null; } }
         public System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object?>> Tags  { get { throw null; } }
         public System.Collections.Generic.IEnumerable<System.Diagnostics.ActivityLink> Links  { get { throw null; } }
+        public System.Diagnostics.ActivityTagsCollection SamplingTags { get { throw null; } }
+        public System.Diagnostics.ActivityTraceId TraceId { get { throw null; } }
     }
-    public delegate System.Diagnostics.ActivityDataRequest GetRequestedData<T>(ref System.Diagnostics.ActivityCreationOptions<T> options);
+    public delegate System.Diagnostics.ActivitySamplingResult SampleActivity<T>(ref System.Diagnostics.ActivityCreationOptions<T> options);
     public sealed class ActivityListener : IDisposable
     {
         public ActivityListener() { throw null; }
         public System.Action<System.Diagnostics.Activity>? ActivityStarted { get { throw null; } set { throw null; } }
         public System.Action<System.Diagnostics.Activity>? ActivityStopped { get { throw null; } set { throw null; } }
         public System.Func<System.Diagnostics.ActivitySource, bool>? ShouldListenTo { get { throw null; } set { throw null; } }
-        public System.Diagnostics.GetRequestedData<string>? GetRequestedDataUsingParentId { get { throw null; } set { throw null; } }
-        public System.Diagnostics.GetRequestedData<ActivityContext>? GetRequestedDataUsingContext { get { throw null; } set { throw null; } }
-        public bool AutoGenerateRootContextTraceId { get { throw null; } set { throw null; } }
+        public System.Diagnostics.SampleActivity<string>? SampleUsingParentId { get { throw null; } set { throw null; } }
+        public System.Diagnostics.SampleActivity<ActivityContext>? Sample { get { throw null; } set { throw null; } }
         public void Dispose() { throw null; }
    }
 }
index 275bebe..d8eaff0 100644 (file)
@@ -38,7 +38,7 @@
     <Compile Include="System\Diagnostics\ActivityTagsCollection.cs" />
     <Compile Include="System\Diagnostics\ActivityContext.cs" />
     <Compile Include="System\Diagnostics\ActivityCreationOptions.cs" />
-    <Compile Include="System\Diagnostics\ActivityDataRequest.cs" />
+    <Compile Include="System\Diagnostics\ActivitySamplingResult.cs" />
     <Compile Include="System\Diagnostics\ActivityEvent.cs" />
     <Compile Include="System\Diagnostics\ActivityKind.cs" />
     <Compile Include="System\Diagnostics\ActivityLink.cs" />
index 2383495..a97c222 100644 (file)
@@ -929,7 +929,7 @@ namespace System.Diagnostics
 
         internal static Activity CreateAndStart(ActivitySource source, string name, ActivityKind kind, string? parentId, ActivityContext parentContext,
                                                 IEnumerable<KeyValuePair<string, object?>>? tags, IEnumerable<ActivityLink>? links,
-                                                DateTimeOffset startTime, ActivityDataRequest request)
+                                                DateTimeOffset startTime, ActivityTagsCollection? samplerTags, ActivitySamplingResult request)
         {
             Activity activity = new Activity(name);
 
@@ -994,11 +994,23 @@ namespace System.Diagnostics
                 }
             }
 
+            if (samplerTags != null)
+            {
+                if (activity._tags == null)
+                {
+                    activity._tags = new TagsLinkedList(samplerTags!);
+                }
+                else
+                {
+                    activity._tags.Add(samplerTags!);
+                }
+            }
+
             activity.StartTimeUtc = startTime == default ? DateTime.UtcNow : startTime.UtcDateTime;
 
-            activity.IsAllDataRequested = request == ActivityDataRequest.AllData || request == ActivityDataRequest.AllDataAndRecorded;
+            activity.IsAllDataRequested = request == ActivitySamplingResult.AllData || request == ActivitySamplingResult.AllDataAndRecorded;
 
-            if (request == ActivityDataRequest.AllDataAndRecorded)
+            if (request == ActivitySamplingResult.AllDataAndRecorded)
             {
                 activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
             }
@@ -1295,6 +1307,34 @@ namespace System.Diagnostics
                 }
             }
 
+            public TagsLinkedList(IEnumerable<KeyValuePair<string, object?>> list) => Add(list);
+
+            // Add doesn't take the lock because it is called from the Activity creation before sharing the activity object to the caller.
+            public void Add(IEnumerable<KeyValuePair<string, object?>> list)
+            {
+                IEnumerator<KeyValuePair<string, object?>> e = list.GetEnumerator();
+                if (!e.MoveNext())
+                {
+                    return;
+                }
+
+                if (_first == null)
+                {
+                    _last = _first = new LinkedListNode<KeyValuePair<string, object?>>(e.Current);
+                }
+                else
+                {
+                    _last!.Next = new LinkedListNode<KeyValuePair<string, object?>>(e.Current);
+                    _last = _last.Next;
+                }
+
+                while (e.MoveNext())
+                {
+                    _last.Next = new LinkedListNode<KeyValuePair<string, object?>>(e.Current);
+                    _last = _last.Next;
+                }
+            }
+
             public void Add(KeyValuePair<string, object?> value)
             {
                 LinkedListNode<KeyValuePair<string, object?>> newNode = new LinkedListNode<KeyValuePair<string, object?>>(value);
index 5d48459..a578542 100644 (file)
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 
 namespace System.Diagnostics
 {
@@ -10,6 +11,9 @@ namespace System.Diagnostics
     /// </summary>
     public readonly struct ActivityCreationOptions<T>
     {
+        private readonly ActivityTagsCollection? _samplerTags;
+        private readonly ActivityContext _context;
+
         /// <summary>
         /// Construct a new <see cref="ActivityCreationOptions{T}"/> object.
         /// </summary>
@@ -27,6 +31,22 @@ namespace System.Diagnostics
             Parent = parent;
             Tags = tags;
             Links = links;
+
+            _samplerTags = null;
+
+            if (parent is ActivityContext ac)
+            {
+                _context = ac;
+            }
+            else if (parent is string p && p != null)
+            {
+                // We don't care about the return value. we care if _context is initialized accordingly.
+                ActivityContext.TryParse(p, null, out _context);
+            }
+            else
+            {
+                _context = default;
+            }
         }
 
         /// <summary>
@@ -58,5 +78,44 @@ namespace System.Diagnostics
         /// Retrieve the list of <see cref="ActivityLink"/> which requested to create the Activity object with.
         /// </summary>
         public IEnumerable<ActivityLink>? Links { get; }
+
+        public ActivityTagsCollection SamplingTags
+        {
+#if ALLOW_PARTIALLY_TRUSTED_CALLERS
+            [System.Security.SecuritySafeCriticalAttribute]
+#endif
+            get
+            {
+                if (_samplerTags == null)
+                {
+                    // Because the struct is readonly, we cannot directly assign _samplerTags. We have to workaround it by calling Unsafe.AsRef
+                    Unsafe.AsRef(in _samplerTags) = new ActivityTagsCollection();
+                }
+
+                return _samplerTags!;
+            }
+        }
+
+        public ActivityTraceId TraceId
+        {
+#if ALLOW_PARTIALLY_TRUSTED_CALLERS
+            [System.Security.SecuritySafeCriticalAttribute]
+#endif
+            get
+            {
+                if (Parent is ActivityContext && _context == default)
+                {
+                    // Because the struct is readonly, we cannot directly assign _context. We have to workaround it by calling Unsafe.AsRef
+                    Unsafe.AsRef(in _context) = new ActivityContext(ActivityTraceId.CreateRandom(), default, ActivityTraceFlags.None);
+                }
+
+                return _context.TraceId;
+            }
+        }
+
+        // Helper to access the sampling tags. The SamplingTags Getter can allocate when not necessary.
+        internal ActivityTagsCollection? GetSamplingTags() => _samplerTags;
+
+        internal ActivityContext GetContext() => _context;
     }
 }
index 72ff9b2..2ad3850 100644 (file)
@@ -8,7 +8,7 @@ namespace System.Diagnostics
     /// <summary>
     /// Define the callback that can be used in <see cref="ActivityListener"/> to allow deciding to create the Activity objects and with what data state.
     /// </summary>
-    public delegate ActivityDataRequest GetRequestedData<T>(ref ActivityCreationOptions<T> options);
+    public delegate ActivitySamplingResult SampleActivity<T>(ref ActivityCreationOptions<T> options);
 
     /// <summary>
     /// ActivityListener allows listening to the start and stop Activity events and give the oppertunity to decide creating the Activity for sampling scenarios.
@@ -40,20 +40,12 @@ namespace System.Diagnostics
         /// <summary>
         /// Set or get the callback used to decide allowing creating <see cref="Activity"/> objects with specific data state.
         /// </summary>
-        public GetRequestedData<string>? GetRequestedDataUsingParentId { get; set; }
+        public SampleActivity<string>? SampleUsingParentId { get; set; }
 
         /// <summary>
         /// Set or get the callback used to decide allowing creating <see cref="Activity"/> objects with specific data state.
         /// </summary>
-        public GetRequestedData<ActivityContext>? GetRequestedDataUsingContext { get; set; }
-
-        /// <summary>
-        /// Determine if the listener automatically generates a new trace Id before sampling when there is no parent context.
-        /// </summary>
-        /// <remarks>
-        /// If this property is set to true and caused generating a new trace Id, the created <see cref="Activity"/> object from such call will have the same generated trace Id.
-        /// </remarks>
-        public bool AutoGenerateRootContextTraceId { get; set;}
+        public SampleActivity<ActivityContext>? Sample { get; set; }
 
         /// <summary>
         /// Dispose will unregister this <see cref="ActivityListener"/> object from listeneing to <see cref="Activity"/> events.
@@ -7,7 +7,7 @@ namespace System.Diagnostics
     /// Used by ActivityListener to indicate what amount of data should be collected for this Activity
     /// Requesting more data causes greater performance overhead to collect it.
     /// </summary>
-    public enum ActivityDataRequest
+    public enum ActivitySamplingResult
     {
         /// <summary>
         /// The Activity object doesn't need to be created
index ac110fd..d1b4d63 100644 (file)
@@ -113,96 +113,96 @@ namespace System.Diagnostics
             }
 
             Activity? activity = null;
+            ActivityTagsCollection? samplerTags;
 
-            ActivityDataRequest dataRequest = ActivityDataRequest.None;
-            bool? useContext = default;
-            ActivityCreationOptions<ActivityContext> optionsWithContext = default;
+            ActivitySamplingResult samplingResult = ActivitySamplingResult.None;
 
             if (parentId != null)
             {
                 var aco = new ActivityCreationOptions<string>(this, name, parentId, kind, tags, links);
-                listeners.EnumWithFunc((ActivityListener listener, ref ActivityCreationOptions<string> data, ref ActivityDataRequest request, ref bool? canUseContext, ref ActivityCreationOptions<ActivityContext> dataWithContext) => {
-                    GetRequestedData<string>? getRequestedDataUsingParentId = listener.GetRequestedDataUsingParentId;
-                    if (getRequestedDataUsingParentId != null)
+                var acoContext = new ActivityCreationOptions<ActivityContext>(this, name, aco.GetContext(), kind, tags, links);
+
+                listeners.EnumWithFunc((ActivityListener listener, ref ActivityCreationOptions<string> data, ref ActivitySamplingResult result, ref ActivityCreationOptions<ActivityContext> dataWithContext) => {
+                    SampleActivity<string>? sampleUsingParentId = listener.SampleUsingParentId;
+                    if (sampleUsingParentId != null)
                     {
-                        ActivityDataRequest dr = getRequestedDataUsingParentId(ref data);
-                        if (dr > request)
+                        ActivitySamplingResult sr = sampleUsingParentId(ref data);
+                        if (sr > result)
                         {
-                            request = dr;
+                            result = sr;
                         }
-
-                        // Stop the enumeration if we get the max value RecordingAndSampling.
-                        return request != ActivityDataRequest.AllDataAndRecorded;
                     }
                     else
                     {
-                        // In case we have a parent Id and the listener not providing the GetRequestedDataUsingParentId, we'll try to find out if the following conditions are true:
-                        //   - The listener is providing the GetRequestedDataUsingContext callback
-                        //   - Can convert the parent Id to a Context
-                        // Then we can call the listener GetRequestedDataUsingContext callback with the constructed context.
-                        GetRequestedData<ActivityContext>? getRequestedDataUsingContext = listener.GetRequestedDataUsingContext;
-                        if (getRequestedDataUsingContext != null)
+                        // In case we have a parent Id and the listener not providing the SampleUsingParentId, we'll try to find out if the following conditions are true:
+                        //   - The listener is providing the Sample callback
+                        //   - Can convert the parent Id to a Context. ActivityCreationOptions.TraceId != default means parent id converted to a valid context.
+                        // Then we can call the listener Sample callback with the constructed context.
+                        SampleActivity<ActivityContext>? sample = listener.Sample;
+                        if (sample != null && data.GetContext() != default) // data.GetContext() != default means parent Id parsed correctly to a context
                         {
-                            if (!canUseContext.HasValue)
+                            ActivitySamplingResult sr = sample(ref dataWithContext);
+                            if (sr > result)
                             {
-                                canUseContext = Activity.TryConvertIdToContext(parentId, traceState: null, out ActivityContext ctx);
-                                if (canUseContext.Value)
-                                {
-                                    dataWithContext = new ActivityCreationOptions<ActivityContext>(data.Source, data.Name, ctx, data.Kind, data.Tags, data.Links);
-                                }
+                                result = sr;
                             }
+                        }
+                    }
+                }, ref aco, ref samplingResult, ref acoContext);
 
-                            if (canUseContext.Value)
-                            {
-                                ActivityDataRequest dr = getRequestedDataUsingContext(ref dataWithContext);
-                                if (dr > request)
-                                {
-                                    request = dr;
-                                }
-                                // Stop the enumeration if we get the max value RecordingAndSampling.
-                                return request != ActivityDataRequest.AllDataAndRecorded;
-                            }
+                if (context == default && aco.GetContext() != default)
+                {
+                    context = aco.GetContext();
+                    parentId = null;
+                }
+
+                samplerTags = aco.GetSamplingTags();
+                ActivityTagsCollection? atc = acoContext.GetSamplingTags();
+                if (atc != null)
+                {
+                    if (samplerTags == null)
+                    {
+                        samplerTags = atc;
+                    }
+                    else
+                    {
+                        foreach (KeyValuePair<string, object> tag in atc)
+                        {
+                            samplerTags.Add(tag);
                         }
                     }
-                    return true;
-                }, ref aco, ref dataRequest, ref useContext, ref optionsWithContext);
+                }
             }
             else
             {
-                ActivityContext initializedContext = context == default && Activity.Current != null ? Activity.Current.Context : context;
-                optionsWithContext = new ActivityCreationOptions<ActivityContext>(this, name, initializedContext, kind, tags, links);
-                listeners.EnumWithFunc((ActivityListener listener, ref ActivityCreationOptions<ActivityContext> data, ref ActivityDataRequest request, ref bool? canUseContext, ref ActivityCreationOptions<ActivityContext> dataWithContext) => {
-                    GetRequestedData<ActivityContext>? getRequestedDataUsingContext = listener.GetRequestedDataUsingContext;
-                    if (getRequestedDataUsingContext != null)
+                bool useCurrentActivityContext = context == default && Activity.Current != null;
+                var aco = new ActivityCreationOptions<ActivityContext>(this, name, useCurrentActivityContext ? Activity.Current!.Context : context, kind, tags, links);
+                listeners.EnumWithFunc((ActivityListener listener, ref ActivityCreationOptions<ActivityContext> data, ref ActivitySamplingResult result, ref ActivityCreationOptions<ActivityContext> unused) => {
+                    SampleActivity<ActivityContext>? sample = listener.Sample;
+                    if (sample != null)
                     {
-                        if (listener.AutoGenerateRootContextTraceId && !canUseContext.HasValue && data.Parent == default)
-                        {
-                            ActivityContext ctx = new ActivityContext(ActivityTraceId.CreateRandom(), default, default);
-                            dataWithContext = new ActivityCreationOptions<ActivityContext>(data.Source, data.Name, ctx, data.Kind, data.Tags, data.Links);
-                            canUseContext = true;
-                        }
-                        ActivityDataRequest dr = getRequestedDataUsingContext(ref data);
-                        if (dr > request)
+                        ActivitySamplingResult dr = sample(ref data);
+                        if (dr > result)
                         {
-                            request = dr;
+                            result = dr;
                         }
-
-                        // Stop the enumeration if we get the max value RecordingAndSampling.
-                        return request != ActivityDataRequest.AllDataAndRecorded;
                     }
-                    return true;
-                }, ref optionsWithContext, ref dataRequest, ref useContext, ref optionsWithContext);
-            }
+                }, ref aco, ref samplingResult, ref aco);
 
-            if (dataRequest != ActivityDataRequest.None)
-            {
-                if (useContext.HasValue && useContext.Value)
+                if (!useCurrentActivityContext)
                 {
-                    context = optionsWithContext.Parent;
-                    parentId = null;
+                    // We use the context stored inside ActivityCreationOptions as it is possible the trace id get automatically generated during the sampling.
+                    // We don't use the context stored inside ActivityCreationOptions only in case if we used Activity.Current context, the reason is we need to
+                    // create the new child activity with Parent set to Activity.Current.
+                    context = aco.GetContext();
                 }
 
-                activity = Activity.CreateAndStart(this, name, kind, parentId, context, tags, links, startTime, dataRequest);
+                samplerTags = aco.GetSamplingTags();
+            }
+
+            if (samplingResult != ActivitySamplingResult.None)
+            {
+                activity = Activity.CreateAndStart(this, name, kind, parentId, context, tags, links, startTime, samplerTags, samplingResult);
                 listeners.EnumWithAction((listener, obj) => listener.ActivityStarted?.Invoke((Activity) obj), activity);
             }
 
@@ -241,7 +241,7 @@ namespace System.Diagnostics
             }
         }
 
-        internal delegate bool Function<T, TParent>(T item, ref ActivityCreationOptions<TParent> data, ref ActivityDataRequest dataRequest, ref bool? ctxInitialized, ref ActivityCreationOptions<ActivityContext> dataWithContext);
+        internal delegate void Function<T, TParent>(T item, ref ActivityCreationOptions<TParent> data, ref ActivitySamplingResult samplingResult, ref ActivityCreationOptions<ActivityContext> dataWithContext);
 
         internal void AddListener(ActivityListener listener)
         {
@@ -333,7 +333,7 @@ namespace System.Diagnostics
 
         public int Count => _list.Count;
 
-        public void EnumWithFunc<TParent>(ActivitySource.Function<T, TParent> func, ref ActivityCreationOptions<TParent> data, ref ActivityDataRequest dataRequest, ref bool? ctxInitialized, ref ActivityCreationOptions<ActivityContext> dataWithContext)
+        public void EnumWithFunc<TParent>(ActivitySource.Function<T, TParent> func, ref ActivityCreationOptions<TParent> data, ref ActivitySamplingResult samplingResult, ref ActivityCreationOptions<ActivityContext> dataWithContext)
         {
             uint version = _version;
             int index = 0;
@@ -356,10 +356,7 @@ namespace System.Diagnostics
 
                 // Important to call the func outside the lock.
                 // This is the whole point we are having this wrapper class.
-                if (!func(item, ref data, ref dataRequest, ref ctxInitialized, ref dataWithContext))
-                {
-                    break;
-                }
+                func(item, ref data, ref samplingResult, ref dataWithContext);
             }
         }
 
index 99bafcd..84d3935 100644 (file)
@@ -60,8 +60,8 @@ namespace System.Diagnostics.Tests
                 listener.ActivityStarted = activity => Assert.NotNull(activity);
                 listener.ActivityStopped = activity => Assert.NotNull(activity);
                 listener.ShouldListenTo = (activitySource) => object.ReferenceEquals(aSource, activitySource);
-                listener.GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivityDataRequest.None;
-                listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.None;
+                listener.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivitySamplingResult.None;
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.None;
 
                 ActivitySource.AddActivityListener(listener);
                 Assert.True(aSource.HasListeners());
@@ -84,8 +84,8 @@ namespace System.Diagnostics.Tests
                     listener.ActivityStarted = activity => counter++;
                     listener.ActivityStopped = activity => counter--;
                     listener.ShouldListenTo = (activitySource) => object.ReferenceEquals(aSource, activitySource);
-                    listener.GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivityDataRequest.AllDataAndRecorded;
-                    listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.AllDataAndRecorded;
+                    listener.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivitySamplingResult.AllDataAndRecorded;
+                    listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.AllDataAndRecorded;
 
                     ActivitySource.AddActivityListener(listener);
 
@@ -141,8 +141,8 @@ namespace System.Diagnostics.Tests
                 listener.ActivityStarted = activity => Assert.NotNull(activity);
                 listener.ActivityStopped = activity => Assert.NotNull(activity);
                 listener.ShouldListenTo = (activitySource) => object.ReferenceEquals(aSource, activitySource);
-                listener.GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivityDataRequest.AllData;
-                listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.AllData;
+                listener.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivitySamplingResult.AllData;
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.AllData;
 
                 ActivitySource.AddActivityListener(listener);
 
@@ -163,8 +163,8 @@ namespace System.Diagnostics.Tests
                     listener.ActivityStarted = activity => activityStartCount++;
                     listener.ActivityStopped = activity => activityStopCount++;
                     listener.ShouldListenTo = (activitySource) => activitySource.Name == "" && activitySource.Version == "";
-                    listener.GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivityDataRequest.AllData;
-                    listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.AllData;
+                    listener.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivitySamplingResult.AllData;
+                    listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.AllData;
 
                     ActivitySource.AddActivityListener(listener);
 
@@ -213,8 +213,8 @@ namespace System.Diagnostics.Tests
                     ActivityStarted = (activity) => { activityStartCount++; Assert.NotNull(activity); },
                     ActivityStopped = (activity) => { activityStopCount++; Assert.NotNull(activity); },
                     ShouldListenTo = (activitySource) => true,
-                    GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivityDataRequest.None,
-                    GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.None
+                    SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivitySamplingResult.None,
+                    Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.None
                 };
                 ActivitySource.AddActivityListener(listeners[0]);
 
@@ -228,8 +228,8 @@ namespace System.Diagnostics.Tests
                     ActivityStarted = (activity) => { activityStartCount++; Assert.NotNull(activity); },
                     ActivityStopped = (activity) => { activityStopCount++; Assert.NotNull(activity); },
                     ShouldListenTo = (activitySource) => true,
-                    GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivityDataRequest.PropagationData,
-                    GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.PropagationData
+                    SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivitySamplingResult.PropagationData,
+                    Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.PropagationData
                 };
                 ActivitySource.AddActivityListener(listeners[1]);
 
@@ -250,8 +250,8 @@ namespace System.Diagnostics.Tests
                     ActivityStarted = (activity) => { activityStartCount++; Assert.NotNull(activity); },
                     ActivityStopped = (activity) => { activityStopCount++; Assert.NotNull(activity); },
                     ShouldListenTo = (activitySource) => true,
-                    GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivityDataRequest.AllData,
-                    GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.AllData
+                    SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivitySamplingResult.AllData,
+                    Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.AllData
                 };
                 ActivitySource.AddActivityListener(listeners[2]);
 
@@ -272,8 +272,8 @@ namespace System.Diagnostics.Tests
                     ActivityStarted = (activity) => { activityStartCount++; Assert.NotNull(activity); },
                     ActivityStopped = (activity) => { activityStopCount++; Assert.NotNull(activity); },
                     ShouldListenTo = (activitySource) => true,
-                    GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivityDataRequest.AllDataAndRecorded,
-                    GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.AllDataAndRecorded
+                    SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivitySamplingResult.AllDataAndRecorded,
+                    Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.AllDataAndRecorded
                 };
                 ActivitySource.AddActivityListener(listeners[3]);
 
@@ -307,8 +307,8 @@ namespace System.Diagnostics.Tests
                 listener.ActivityStarted = activity => Assert.NotNull(activity);
                 listener.ActivityStopped = activity => Assert.NotNull(activity);
                 listener.ShouldListenTo = (activitySource) => true;
-                listener.GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivityDataRequest.AllData;
-                listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.AllData;
+                listener.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => ActivitySamplingResult.AllData;
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.AllData;
 
                 ActivitySource.AddActivityListener(listener);
 
@@ -363,7 +363,7 @@ namespace System.Diagnostics.Tests
                 using ActivityListener listener = new ActivityListener();
 
                 listener.ShouldListenTo = (activitySource) => activitySource.Name == "ParentContext";
-                listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
                 {
                     Activity c = Activity.Current;
                     if (c != null)
@@ -371,11 +371,10 @@ namespace System.Diagnostics.Tests
                         Assert.Equal(c.Context, activityOptions.Parent);
                     }
 
-                    return ActivityDataRequest.AllData;
+                    return ActivitySamplingResult.AllData;
                 };
 
                 ActivitySource.AddActivityListener(listener);
-
                 using Activity a = aSource.StartActivity("a", ActivityKind.Server, new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), 0));
                 using Activity b = aSource.StartActivity("b");
                 Assert.Equal(a.Context, b.Parent.Context);
@@ -426,19 +425,19 @@ namespace System.Diagnostics.Tests
                 using ActivityListener listener3 = new ActivityListener();  // will have both context and parent Id callbacks
 
                 listener1.ShouldListenTo = listener2.ShouldListenTo = listener3.ShouldListenTo = (activitySource) => activitySource.Name == "ParentIdsTest";
-                listener1.GetRequestedDataUsingContext = listener3.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                listener1.Sample = listener3.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
                 {
                     callingByContext++;
 
                     Assert.Equal(new ActivityContext(ActivityTraceId.CreateFromString(w3cId.AsSpan(3,  32)), ActivitySpanId.CreateFromString(w3cId.AsSpan(36, 16)), ActivityTraceFlags.Recorded),
                                  activityOptions.Parent);
 
-                    return ActivityDataRequest.AllData;
+                    return ActivitySamplingResult.AllData;
                 };
-                listener2.GetRequestedDataUsingParentId = listener3.GetRequestedDataUsingParentId = (ref ActivityCreationOptions<string> activityOptions) =>
+                listener2.SampleUsingParentId = listener3.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) =>
                 {
                     callingByParentId++;
-                    return ActivityDataRequest.AllData;
+                    return ActivitySamplingResult.AllData;
                 };
 
                 ActivitySource.AddActivityListener(listener1);
@@ -476,10 +475,10 @@ namespace System.Diagnostics.Tests
                 using ActivityListener listener = new ActivityListener();
                 listener.ShouldListenTo = (activitySource) => activitySource.Name == "RemoteContext";
 
-                listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
                 {
                     isRemote = activityOptions.Parent.IsRemote;
-                    return ActivityDataRequest.AllData;
+                    return ActivitySamplingResult.AllData;
                 };
 
                 ActivitySource.AddActivityListener(listener);
@@ -506,10 +505,12 @@ namespace System.Diagnostics.Tests
 
                 ActivityContext ctx = default;
 
-                listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                bool forceGenerateTraceId = false;
+
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
                 {
-                    ctx = activityOptions.Parent;
-                    return ActivityDataRequest.AllData;
+                    ctx = forceGenerateTraceId ? new ActivityContext(activityOptions.TraceId, default, default) : activityOptions.Parent;
+                    return ActivitySamplingResult.AllData;
                 };
 
                 ActivitySource.AddActivityListener(listener);
@@ -519,7 +520,7 @@ namespace System.Diagnostics.Tests
                     Assert.Equal(default, ctx);
                 }
 
-                listener.AutoGenerateRootContextTraceId = true;
+                forceGenerateTraceId = true;
 
                 Activity activity = aSource.StartActivity("a2", default, ctx);
 
@@ -542,13 +543,12 @@ namespace System.Diagnostics.Tests
 
                 ActivityContext ctx = default;
 
-                listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
                 {
-                    ctx = activityOptions.Parent;
-                    return ActivityDataRequest.AllData;
+                    ctx = new ActivityContext(activityOptions.TraceId, default, default);
+                    return ActivitySamplingResult.AllData;
                 };
 
-                listener.AutoGenerateRootContextTraceId = true;
                 ActivitySource.AddActivityListener(listener);
 
                 Activity activity = aSource.StartActivity("a2", default, null);
@@ -573,7 +573,7 @@ namespace System.Diagnostics.Tests
                 using ActivityListener listener = new ActivityListener();
 
                 listener.ShouldListenTo = (activitySource) => activitySource.Name == "EventNotificationOrder";
-                listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivityDataRequest.AllData;
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => ActivitySamplingResult.AllData;
                 listener.ActivityStopped = a => Assert.Equal(child, Activity.Current);
 
                 ActivitySource.AddActivityListener(listener);
@@ -590,6 +590,211 @@ namespace System.Diagnostics.Tests
             }).Dispose();
         }
 
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void TestAddingSamplerTags()
+        {
+            RemoteExecutor.Invoke(() => {
+
+                using ActivitySource aSource = new ActivitySource("SamplerTags1");
+                using ActivityListener listener = new ActivityListener();
+                listener.ShouldListenTo = (activitySource) => activitySource.Name == "SamplerTags1";
+
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                {
+                    activityOptions.SamplingTags.Add("tag1", "value1");
+                    activityOptions.SamplingTags.Add("tag2", "value2");
+                    return ActivitySamplingResult.AllData;
+                };
+
+                ActivitySource.AddActivityListener(listener);
+
+                using Activity activity = aSource.StartActivity("a1");
+
+                Assert.NotNull(activity);
+                Assert.Equal(2, activity.TagObjects.Count());
+                Assert.Equal(new KeyValuePair<string, object>("tag1", "value1"), activity.TagObjects.ElementAt(0));
+                Assert.Equal(new KeyValuePair<string, object>("tag2", "value2"), activity.TagObjects.ElementAt(1));
+            }).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void TestAddingSamplerTagsUsingParentId()
+        {
+            RemoteExecutor.Invoke(() => {
+
+                using ActivitySource aSource = new ActivitySource("SamplerTags2");
+                using ActivityListener listener = new ActivityListener();
+                listener.ShouldListenTo = (activitySource) => activitySource.Name == "SamplerTags2";
+
+                listener.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) =>
+                {
+                    activityOptions.SamplingTags.Add("tag1", "value1");
+                    activityOptions.SamplingTags.Add("tag2", "value2");
+                    return ActivitySamplingResult.AllData;
+                };
+
+                ActivitySource.AddActivityListener(listener);
+
+                Activity activity = aSource.StartActivity("a1", ActivityKind.Client, "SomeParent");
+
+                Assert.NotNull(activity);
+                Assert.Equal(2, activity.TagObjects.Count());
+                Assert.Equal(new KeyValuePair<string, object>("tag1", "value1"), activity.TagObjects.ElementAt(0));
+                Assert.Equal(new KeyValuePair<string, object>("tag2", "value2"), activity.TagObjects.ElementAt(1));
+            }).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void TestNotAddedSamplerTags()
+        {
+            RemoteExecutor.Invoke(() => {
+
+                using ActivitySource aSource = new ActivitySource("SamplerTags3");
+                using ActivityListener listener = new ActivityListener();
+                listener.ShouldListenTo = (activitySource) => activitySource.Name == "SamplerTags3";
+
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                {
+                    activityOptions.SamplingTags.Add("tag1", "value1");
+                    Assert.False(true, "This callback shouldn't be called at all.");
+                    return ActivitySamplingResult.AllData;
+                };
+
+                ActivitySource.AddActivityListener(listener);
+
+                // activity should be null as no listener asked for creation.
+                using Activity activity = aSource.StartActivity("a1", ActivityKind.Client, "NonW3CParentId");
+
+                Assert.Null(activity);
+            }).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void TestDefaultTraceIdWithHierarchicalParentId()
+        {
+            RemoteExecutor.Invoke(() => {
+
+                using ActivitySource aSource = new ActivitySource("SamplerTags4");
+                using ActivityListener listener = new ActivityListener();
+                listener.ShouldListenTo = (activitySource) => activitySource.Name == "SamplerTags4";
+
+                listener.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) =>
+                {
+                    Assert.Equal(default, activityOptions.TraceId);
+                    return ActivitySamplingResult.AllData;
+                };
+
+                ActivitySource.AddActivityListener(listener);
+
+                Activity activity = aSource.StartActivity("a1", ActivityKind.Client, "HierarchicalParentId");
+
+                Assert.NotNull(activity);
+                // activity has no parent. expected default trace Id.
+                Assert.Equal(default, activity.TraceId);
+            }).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void TestAddingSamplerTagsFromMultipleListeners()
+        {
+            RemoteExecutor.Invoke(() => {
+                using ActivitySource aSource = new ActivitySource("SamplerTags5");
+                using ActivityListener listener1 = new ActivityListener();
+                using ActivityListener listener2 = new ActivityListener();
+                listener1.ShouldListenTo = (activitySource) => activitySource.Name == "SamplerTags5";
+                listener2.ShouldListenTo = (activitySource) => activitySource.Name == "SamplerTags5";
+
+                listener1.Sample = listener2.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                {
+                    if (activityOptions.SamplingTags.Count == 0)
+                        activityOptions.SamplingTags.Add("tag1", "value1");
+                    else
+                        activityOptions.SamplingTags.Add("tag2", "value2");
+
+                    return ActivitySamplingResult.AllData;
+                };
+
+                ActivitySource.AddActivityListener(listener1);
+                ActivitySource.AddActivityListener(listener2);
+
+                using Activity activity = aSource.StartActivity("a1");
+
+                Assert.NotNull(activity);
+                Assert.Equal(2, activity.TagObjects.Count());
+                Assert.Equal(new KeyValuePair<string, object>("tag1", "value1"), activity.TagObjects.ElementAt(0));
+                Assert.Equal(new KeyValuePair<string, object>("tag2", "value2"), activity.TagObjects.ElementAt(1));
+            }).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void TestSamplerTagsWithMixedListenerModels()
+        {
+            RemoteExecutor.Invoke(() => {
+                const string w3cId = "00-99d43cb30a4cdb4fbeee3a19c29201b0-e82825765f051b47-01";
+
+                using ActivitySource aSource = new ActivitySource("SamplerTags6");
+                using ActivityListener listener1 = new ActivityListener();
+                using ActivityListener listener2 = new ActivityListener();
+                listener1.ShouldListenTo = (activitySource) => activitySource.Name == "SamplerTags6";
+                listener2.ShouldListenTo = (activitySource) => activitySource.Name == "SamplerTags6";
+
+                // listener1 provide SampleUsingParentId callback and doesn't provide Sample
+                listener1.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) =>
+                {
+                    activityOptions.SamplingTags.Add("tag1", "value1");
+                    return ActivitySamplingResult.AllData;
+                };
+
+                // listener2 provide Sample callback and doesn't provide SampleUsingParentId
+                // Sample should get called as we convert the w3c id to a context.
+                listener2.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                {
+                    activityOptions.SamplingTags.Add("tag2", "value2");
+                    return ActivitySamplingResult.AllData;
+                };
+
+                ActivitySource.AddActivityListener(listener1);
+                ActivitySource.AddActivityListener(listener2);
+
+                using Activity activity = aSource.StartActivity("a1", ActivityKind.Client, w3cId);
+
+                Assert.NotNull(activity);
+                Assert.Equal(2, activity.TagObjects.Count());
+                Assert.Equal(new KeyValuePair<string, object>("tag1", "value1"), activity.TagObjects.ElementAt(0));
+                Assert.Equal(new KeyValuePair<string, object>("tag2", "value2"), activity.TagObjects.ElementAt(1));
+            }).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void TestAddSamplerAndActivityCreationTags()
+        {
+            RemoteExecutor.Invoke(() => {
+                using ActivitySource aSource = new ActivitySource("SamplerTags7");
+                using ActivityListener listener = new ActivityListener();
+                listener.ShouldListenTo = (activitySource) => activitySource.Name == "SamplerTags7";
+
+                listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
+                {
+                    activityOptions.SamplingTags.Add("SamplerTag1", "SamplerValue1");
+                    return ActivitySamplingResult.AllData;
+                };
+
+                ActivitySource.AddActivityListener(listener);
+
+                ActivityTagsCollection tags = new ActivityTagsCollection();
+                tags["tag1"] = "value1";
+                tags["tag2"] = "value2";
+
+                using Activity activity = aSource.StartActivity("a1", ActivityKind.Client, default(ActivityContext), tags);
+
+                Assert.NotNull(activity);
+                Assert.Equal(3, activity.TagObjects.Count());
+                Assert.Equal(new KeyValuePair<string, object>("tag1", "value1"), activity.TagObjects.ElementAt(0));
+                Assert.Equal(new KeyValuePair<string, object>("tag2", "value2"), activity.TagObjects.ElementAt(1));
+                Assert.Equal(new KeyValuePair<string, object>("SamplerTag1", "SamplerValue1"), activity.TagObjects.ElementAt(2));
+            }).Dispose();
+        }
+
         public void Dispose() => Activity.Current = null;
     }
 }