File: System\Diagnostics\DiagnosticSourceEventSource.cs
Web Access
Project: src\src\libraries\System.Diagnostics.DiagnosticSource\src\System.Diagnostics.DiagnosticSource.csproj (System.Diagnostics.DiagnosticSource)
// 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;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
 
namespace System.Diagnostics
{
    /// <summary>
    /// DiagnosticSourceEventSource serves two purposes
    ///
    ///   1) It allows debuggers to inject code via Function evaluation. This is the purpose of the
    ///   BreakPointWithDebuggerFuncEval function in the 'OnEventCommand' method. Basically even in
    ///   release code, debuggers can place a breakpoint in this method and then trigger the
    ///   DiagnosticSourceEventSource via ETW. Thus from outside the process you can get a hook that
    ///   is guaranteed to happen BEFORE any DiagnosticSource events (if the process is just starting)
    ///   or as soon as possible afterward if it is on attach.
    ///
    ///   2) It provides a 'bridge' that allows DiagnosticSource messages to be forwarded to EventListers
    ///   or ETW. You can do this by enabling the Microsoft-Diagnostics-DiagnosticSource with the
    ///   'Events' keyword (for diagnostics purposes, you should also turn on the 'Messages' keyword.
    ///
    ///   This EventSource defines a EventSource argument called 'FilterAndPayloadSpecs' that defines
    ///   what DiagnosticSources to enable and what parts of the payload to serialize into the key-value
    ///   list that will be forwarded to the EventSource. If it is empty, values of properties of the
    ///   diagnostic source payload are dumped as strings (using ToString()) and forwarded to the EventSource.
    ///   For what people think of as serializable object strings, primitives this gives you want you want.
    ///   (the value of the property in string form) for what people think of as non-serializable objects
    ///   (e.g. HttpContext) the ToString() method is typically not defined, so you get the Object.ToString()
    ///   implementation that prints the type name. This is useful since this is the information you need
    ///   (the type of the property) to discover the field names so you can create a transform specification
    ///   that will pick off the properties you desire.
    ///
    ///   Once you have the particular values you desire, the implicit payload elements are typically not needed
    ///   anymore and you can prefix the Transform specification with a '-' which suppresses the implicit
    ///   transform (you only get the values of the properties you specifically ask for.
    ///
    ///   Logically a transform specification is simply a fetching specification X.Y.Z along with a name to give
    ///   it in the output (which defaults to the last name in the fetch specification).
    ///
    ///   The FilterAndPayloadSpecs is one long string with the following structures
    ///
    ///   * It is a newline separated list of FILTER_AND_PAYLOAD_SPEC
    ///   * a FILTER_AND_PAYLOAD_SPEC can be
    ///       * EVENT_NAME : TRANSFORM_SPECS
    ///       * EMPTY - turns on all sources with implicit payload elements.
    ///   * an EVENTNAME can be
    ///       * DIAGNOSTIC_SOURCE_NAME / DIAGNOSTIC_EVENT_NAME @ EVENT_SOURCE_EVENTNAME - give the name as well as the EventSource event to log it under.
    ///       * DIAGNOSTIC_SOURCE_NAME / DIAGNOSTIC_EVENT_NAME
    ///       * DIAGNOSTIC_SOURCE_NAME    - which wildcards every event in the Diagnostic source or
    ///       * EMPTY                     - which turns on all sources
    ///     Or it can be "[AS] ACTIVITY_SOURCE_NAME + ACTIVITY_NAME / ACTIVITY_EVENT_NAME - SAMPLING_RESULT"
    ///       * All parts are optional and can be empty string.
    ///       * ACTIVITY_SOURCE_NAME can be "*" to listen to all ActivitySources
    ///       * ACTIVITY_SOURCE_NAME can be empty string which will listen to ActivitySource that create Activities using "new Activity(...)"
    ///       * ACTIVITY_NAME is the activity operation name to filter with.
    ///       * ACTIVITY_EVENT_NAME either "Start" to listen to Activity Start event, or "Stop" to listen to Activity Stop event, or empty string to listen to both Start and Stop Activity events.
    ///       * SAMPLING_RESULT either "Propagate" to create the Activity with PropagationData, or "Record" to create the Activity with AllData, or empty string to create the Activity with AllDataAndRecorded
    ///   * TRANSFORM_SPEC is a semicolon separated list of TRANSFORM_SPEC, which can be
    ///       * - TRANSFORM_SPEC               - the '-' indicates that implicit payload elements should be suppressed
    ///       * VARIABLE_NAME = PROPERTY_SPEC  - indicates that a payload element 'VARIABLE_NAME' is created from PROPERTY_SPEC
    ///       * PROPERTY_SPEC                  - This is a shortcut where VARIABLE_NAME is the LAST property name
    ///   * a PROPERTY_SPEC is basically a list of names separated by '.'
    ///       * PROPERTY_NAME                  - fetches a property from the DiagnosticSource payload object
    ///       * PROPERTY_NAME . PROPERTY NAME  - fetches a sub-property of the object.
    ///
    ///       * *Activity                      - fetches Activity.Current
    ///       * *Enumerate                     - enumerates all the items in an IEnumerable, calls ToString() on them, and joins the
    ///                                          strings in a comma separated list.
    /// Example1:
    ///
    ///    "BridgeTestSource1/TestEvent1:cls_Point_X=cls.Point.X;cls_Point_Y=cls.Point.Y\r\n" +
    ///    "BridgeTestSource2/TestEvent2:-cls.Url"
    ///
    /// This indicates that two events should be turned on, The 'TestEvent1' event in BridgeTestSource1 and the
    /// 'TestEvent2' in BridgeTestSource2. In the first case, because the transform did not begin with a -
    /// any primitive type/string of 'TestEvent1's payload will be serialized into the output. In addition if
    /// there a property of the payload object called 'cls' which in turn has a property 'Point' which in turn
    /// has a property 'X' then that data is also put in the output with the name cls_Point_X. Similarly
    /// if cls.Point.Y exists, then that value will also be put in the output with the name cls_Point_Y.
    ///
    /// For the 'BridgeTestSource2/TestEvent2' event, because the - was specified NO implicit fields will be
    /// generated, but if there is a property call 'cls' which has a property 'Url' then that will be placed in
    /// the output with the name 'Url' (since that was the last property name used and no Variable= clause was
    /// specified.
    ///
    /// Example:
    ///
    ///     "BridgeTestSource1\r\n" +
    ///     "BridgeTestSource2"
    ///
    /// This will enable all events for the BridgeTestSource1 and BridgeTestSource2 sources. Any string/primitive
    /// properties of any of the events will be serialized into the output.
    ///
    /// Example:
    ///
    ///     ""
    ///
    /// This turns on all DiagnosticSources Any string/primitive properties of any of the events will be serialized
    /// into the output. This is not likely to be a good idea as it will be very verbose, but is useful to quickly
    /// discover what is available.
    ///
    /// Example:
    ///     "[AS]*"                      listen to all ActivitySources and all Activities events (Start/Stop). Activities will be created with AllDataAndRecorded sampling.
    ///     "[AS]"                       listen to default ActivitySource and Activities events (Start/Stop) while the Activity is created using "new Activity(...)". Such Activities will be created with AllDataAndRecorded sampling.
    ///     "[AS]MyLibrary/Start"        listen to `MyLibrary` ActivitySource and the 'Start' Activity event. The Activities will be created with AllDataAndRecorded sampling.
    ///     "[AS]MyLibrary/-Propagate"   listen to `MyLibrary` ActivitySource and the 'Start and Stop' Activity events. The Activities will be created with PropagationData sampling.
    ///     "[AS]MyLibrary/Stop-Record"  listen to `MyLibrary` ActivitySource and the 'Stop' Activity event. The Activities will be created with AllData sampling.
    ///     "[AS]*/-"                    listen to all ActivitySources and the Start and Stop Activity events. Activities will be created with AllDataAndRecorded sampling. this equivalent to "[AS]*" too.
    ///     "[AS]*+MyActivity"           listen to all activity sources when creating Activity with the operation name "MyActivity".
    ///
    /// * How data is logged in the EventSource
    ///
    /// By default all data from DiagnosticSources is logged to the DiagnosticEventSource event called 'Event'
    /// which has three fields
    ///
    ///     string SourceName,
    ///     string EventName,
    ///     IEnumerable[KeyValuePair[string, string]] Argument
    ///
    /// However to support start-stop activity tracking, there are six other events that can be used
    ///
    ///     Activity1Start
    ///     Activity1Stop
    ///     Activity2Start
    ///     Activity2Stop
    ///     RecursiveActivity1Start
    ///     RecursiveActivity1Stop
    ///
    /// By using the SourceName/EventName@EventSourceName syntax, you can force particular DiagnosticSource events to
    /// be logged with one of these EventSource events. This is useful because the events above have start-stop semantics
    /// which means that they create activity IDs that are attached to all logging messages between the start and
    /// the stop (see https://blogs.msdn.microsoft.com/vancem/2015/09/14/exploring-eventsource-activity-correlation-and-causation-features/)
    ///
    /// For example the specification
    ///
    ///     "MyDiagnosticSource/RequestStart@Activity1Start\r\n" +
    ///     "MyDiagnosticSource/RequestStop@Activity1Stop\r\n" +
    ///     "MyDiagnosticSource/SecurityStart@Activity2Start\r\n" +
    ///     "MyDiagnosticSource/SecurityStop@Activity2Stop\r\n"
    ///
    /// Defines that RequestStart will be logged with the EventSource Event Activity1Start (and the corresponding stop) which
    /// means that all events caused between these two markers will have an activity ID associated with this start event.
    /// Similarly SecurityStart is mapped to Activity2Start.
    ///
    /// Note you can map many DiagnosticSource events to the same EventSource Event (e.g. Activity1Start). As long as the
    /// activities don't nest, you can reuse the same event name (since the payloads have the DiagnosticSource name which can
    /// disambiguate). However if they nest you need to use another EventSource event because the rules of EventSource
    /// activities state that a start of the same event terminates any existing activity of the same name.
    ///
    /// As its name suggests RecursiveActivity1Start, is marked as recursive and thus can be used when the activity can nest with
    /// itself. This should not be a 'top most' activity because it is not 'self healing' (if you miss a stop, then the
    /// activity NEVER ends).
    ///
    /// See the DiagnosticSourceEventSourceBridgeTest.cs for more explicit examples of using this bridge.
    /// </summary>
    [EventSource(Name = "Microsoft-Diagnostics-DiagnosticSource")]
    // These suppressions can go away with https://github.com/mono/linker/issues/2175
    [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2113:ReflectionToRequiresUnreferencedCode",
        Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves methods on Delegate and MulticastDelegate " +
                        "because the nested type OverrideEventProvider's base type EventProvider defines a delegate. " +
                        "This includes Delegate and MulticastDelegate methods which require unreferenced code, but " +
                        "EnsureDescriptorsInitialized does not access these members and is safe to call.")]
    [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2115:ReflectionToDynamicallyAccessedMembers",
        Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves methods on Delegate and MulticastDelegate " +
                        "because the nested type OverrideEventProvider's base type EventProvider defines a delegate. " +
                        "This includes Delegate and MulticastDelegate methods which have dynamically accessed members requirements, but " +
                        "EnsureDescriptorsInitialized does not access these members and is safe to call.")]
    internal sealed class DiagnosticSourceEventSource : EventSource
    {
        public static readonly DiagnosticSourceEventSource Log = new DiagnosticSourceEventSource();
 
        public static class Keywords
        {
            /// <summary>
            /// Indicates diagnostics messages from DiagnosticSourceEventSource should be included.
            /// </summary>
            public const EventKeywords Messages = (EventKeywords)0x1;
            /// <summary>
            /// Indicates that all events from all diagnostic sources should be forwarded to the EventSource using the 'Event' event.
            /// </summary>
            public const EventKeywords Events = (EventKeywords)0x2;
 
            // Some ETW logic does not support passing arguments to the EventProvider. To get around
            // this in common cases, we define some keywords that basically stand in for particular common arguments
            // That way at least the common cases can be used by everyone (and it also compresses things).
            // We start these keywords at 0x1000. See below for the values these keywords represent
            // Because we want all keywords on to still mean 'dump everything by default' we have another keyword
            // IgnoreShorcutKeywords which must be OFF in order for the shortcuts to work thus the all 1s keyword
            // still means what you expect.
            public const EventKeywords IgnoreShortCutKeywords = (EventKeywords)0x0800;
            public const EventKeywords AspNetCoreHosting = (EventKeywords)0x1000;
            public const EventKeywords EntityFrameworkCoreCommands = (EventKeywords)0x2000;
        };
 
        // Setting AspNetCoreHosting is like having this in the FilterAndPayloadSpecs string
        // It turns on basic hosting events.
        private readonly string AspNetCoreHostingKeywordValue =
            "Microsoft.AspNetCore/Microsoft.AspNetCore.Hosting.BeginRequest@Activity1Start:-" +
                "httpContext.Request.Method;" +
                "httpContext.Request.Host;" +
                "httpContext.Request.Path;" +
                "httpContext.Request.QueryString" +
            "\n" +
            "Microsoft.AspNetCore/Microsoft.AspNetCore.Hosting.EndRequest@Activity1Stop:-" +
                "httpContext.TraceIdentifier;" +
                "httpContext.Response.StatusCode";
 
        // Setting EntityFrameworkCoreCommands is like having this in the FilterAndPayloadSpecs string
        // It turns on basic SQL commands.
        private readonly string EntityFrameworkCoreCommandsKeywordValue =
            "Microsoft.EntityFrameworkCore/Microsoft.EntityFrameworkCore.BeforeExecuteCommand@Activity2Start:-" +
                "Command.Connection.DataSource;" +
                "Command.Connection.Database;" +
                "Command.CommandText" +
            "\n" +
            "Microsoft.EntityFrameworkCore/Microsoft.EntityFrameworkCore.AfterExecuteCommand@Activity2Stop:-";
 
        /// <summary>
        /// Used to send ad-hoc diagnostics to humans.
        /// </summary>
        [Event(1, Keywords = Keywords.Messages)]
        public void Message(string? Message)
        {
            WriteEvent(1, Message);
        }
 
        /// <summary>
        /// Events from DiagnosticSource can be forwarded to EventSource using this event.
        /// </summary>
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Arguments parameter is preserved by DynamicDependency")]
        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))]
        [Event(2, Keywords = Keywords.Events)]
        private void Event(string SourceName, string EventName, IEnumerable<KeyValuePair<string, string?>>? Arguments)
        {
            WriteEvent(2, SourceName, EventName, Arguments);
        }
 
        /// <summary>
        /// This is only used on V4.5 systems that don't have the ability to log KeyValuePairs directly.
        /// It will eventually go away, but we should always reserve the ID for this.
        /// </summary>
        [Event(3, Keywords = Keywords.Events)]
        private void EventJson(string SourceName, string EventName, string ArgmentsJson)
        {
            WriteEvent(3, SourceName, EventName, ArgmentsJson);
        }
 
        /// <summary>
        /// Used to mark the beginning of an activity
        /// </summary>
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Arguments parameter is preserved by DynamicDependency")]
        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))]
        [Event(4, Keywords = Keywords.Events)]
        private void Activity1Start(string SourceName, string EventName, IEnumerable<KeyValuePair<string, string?>> Arguments)
        {
            WriteEvent(4, SourceName, EventName, Arguments);
        }
 
        /// <summary>
        /// Used to mark the end of an activity
        /// </summary>
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Arguments parameter is preserved by DynamicDependency")]
        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))]
        [Event(5, Keywords = Keywords.Events)]
        private void Activity1Stop(string SourceName, string EventName, IEnumerable<KeyValuePair<string, string?>> Arguments)
        {
            WriteEvent(5, SourceName, EventName, Arguments);
        }
 
        /// <summary>
        /// Used to mark the beginning of an activity
        /// </summary>
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Arguments parameter is preserved by DynamicDependency")]
        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))]
        [Event(6, Keywords = Keywords.Events)]
        private void Activity2Start(string SourceName, string EventName, IEnumerable<KeyValuePair<string, string?>> Arguments)
        {
            WriteEvent(6, SourceName, EventName, Arguments);
        }
 
        /// <summary>
        /// Used to mark the end of an activity that can be recursive.
        /// </summary>
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Arguments parameter is preserved by DynamicDependency")]
        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))]
        [Event(7, Keywords = Keywords.Events)]
        private void Activity2Stop(string SourceName, string EventName, IEnumerable<KeyValuePair<string, string?>> Arguments)
        {
            WriteEvent(7, SourceName, EventName, Arguments);
        }
 
        /// <summary>
        /// Used to mark the beginning of an activity
        /// </summary>
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Arguments parameter is preserved by DynamicDependency")]
        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))]
        [Event(8, Keywords = Keywords.Events, ActivityOptions = EventActivityOptions.Recursive)]
        private void RecursiveActivity1Start(string SourceName, string EventName, IEnumerable<KeyValuePair<string, string?>> Arguments)
        {
            WriteEvent(8, SourceName, EventName, Arguments);
        }
 
        /// <summary>
        /// Used to mark the end of an activity that can be recursive.
        /// </summary>
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Arguments parameter is preserved by DynamicDependency")]
        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))]
        [Event(9, Keywords = Keywords.Events, ActivityOptions = EventActivityOptions.Recursive)]
        private void RecursiveActivity1Stop(string SourceName, string EventName, IEnumerable<KeyValuePair<string, string?>> Arguments)
        {
            WriteEvent(9, SourceName, EventName, Arguments);
        }
 
        /// <summary>
        /// Fires when a new DiagnosticSource becomes available.
        /// </summary>
        /// <param name="SourceName"></param>
        [Event(10, Keywords = Keywords.Events)]
        private void NewDiagnosticListener(string SourceName)
        {
            WriteEvent(10, SourceName);
        }
 
        /// <summary>
        /// Fires when the Activity start.
        /// </summary>
        /// <param name="SourceName">The ActivitySource name</param>
        /// <param name="ActivityName">The Activity name</param>
        /// <param name="Arguments">Name and value pairs of the Activity properties</param>
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Arguments parameter is preserved by DynamicDependency")]
        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))]
        [Event(11, Keywords = Keywords.Events, ActivityOptions = EventActivityOptions.Recursive)]
        private void ActivityStart(string SourceName, string ActivityName, IEnumerable<KeyValuePair<string, string?>> Arguments) =>
            WriteEvent(11, SourceName, ActivityName, Arguments);
 
        /// <summary>
        /// Fires when the Activity stop.
        /// </summary>
        /// <param name="SourceName">The ActivitySource name</param>
        /// <param name="ActivityName">The Activity name</param>
        /// <param name="Arguments">Name and value pairs of the Activity properties</param>
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Arguments parameter is preserved by DynamicDependency")]
        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(KeyValuePair<,>))]
        [Event(12, Keywords = Keywords.Events, ActivityOptions = EventActivityOptions.Recursive)]
        private void ActivityStop(string SourceName, string ActivityName, IEnumerable<KeyValuePair<string, string?>> Arguments) =>
            WriteEvent(12, SourceName, ActivityName, Arguments);
 
        #region private
 
        private DiagnosticSourceEventSource()
            // This constructor uses EventSourceSettings which is only available on V4.6 and above
            // Use the EventSourceSettings to turn on support for complex types, if available (v4.6 and above).
            : base(EventSourceSettings.EtwSelfDescribingEventFormat)
        {
        }
 
        /// <summary>
        /// Called when the EventSource gets a command from a EventListener or ETW.
        /// </summary>
        [NonEvent]
        protected override void OnEventCommand(EventCommandEventArgs command)
        {
            // On every command (which the debugger can force by turning on this EventSource with ETW)
            // call a function that the debugger can hook to do an arbitrary func evaluation.
            BreakPointWithDebuggerFuncEval();
 
            lock (this)
            {
                if ((command.Command == EventCommand.Update || command.Command == EventCommand.Enable) &&
                    IsEnabled(EventLevel.Informational, Keywords.Events))
                {
                    string? filterAndPayloadSpecs = null;
                    command.Arguments!.TryGetValue("FilterAndPayloadSpecs", out filterAndPayloadSpecs);
 
                    if (!IsEnabled(EventLevel.Informational, Keywords.IgnoreShortCutKeywords))
                    {
                        if (IsEnabled(EventLevel.Informational, Keywords.AspNetCoreHosting))
                            filterAndPayloadSpecs = NewLineSeparate(filterAndPayloadSpecs, AspNetCoreHostingKeywordValue);
                        if (IsEnabled(EventLevel.Informational, Keywords.EntityFrameworkCoreCommands))
                            filterAndPayloadSpecs = NewLineSeparate(filterAndPayloadSpecs, EntityFrameworkCoreCommandsKeywordValue);
                    }
                    FilterAndTransform.CreateFilterAndTransformList(ref _specs, filterAndPayloadSpecs, this);
                }
                else if (command.Command == EventCommand.Update || command.Command == EventCommand.Disable)
                {
                    FilterAndTransform.DestroyFilterAndTransformList(ref _specs, this);
                }
            }
        }
 
        // trivial helper to allow you to join two strings the first of which can be null.
        private static string NewLineSeparate(string? str1, string str2)
        {
            Debug.Assert(str2 != null);
            if (string.IsNullOrEmpty(str1))
                return str2;
            return str1 + "\n" + str2;
        }
 
        #region debugger hooks
        private volatile bool _false;       // A value that is always false but the compiler does not know this.
 
        /// <summary>
        /// A function which is fully interruptible even in release code so we can stop here and
        /// do function evaluation in the debugger. Thus this is just a place that is useful
        /// for the debugger to place a breakpoint where it can inject code with function evaluation
        /// </summary>
        [NonEvent, MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
        private void BreakPointWithDebuggerFuncEval()
        {
            new object();   // This is only here because it helps old .NET Framework runtimes emit a GC safe point at the start of the method
            while (_false)
            {
                _false = false;
            }
        }
        #endregion
 
 
        [Flags]
        internal enum ActivityEvents
        {
            None          = 0x00,
            ActivityStart = 0x01,
            ActivityStop  = 0x02,
            All           = ActivityStart | ActivityStop,
        }
 
        #region EventSource hooks
 
        /// <summary>
        /// FilterAndTransform represents on transformation specification from a DiagnosticsSource
        /// to EventSource's 'Event' method. (e.g. MySource/MyEvent:out=prop1.prop2.prop3).
        /// Its main method is 'Morph' which takes a DiagnosticSource object and morphs it into
        /// a list of string,string key value pairs.
        ///
        /// This method also contains that static 'Create/Destroy FilterAndTransformList, which
        /// simply parse a series of transformation specifications.
        /// </summary>
        internal sealed class FilterAndTransform
        {
            /// <summary>
            /// Parses filterAndPayloadSpecs which is a list of lines each of which has the from
            ///
            ///    DiagnosticSourceName/EventName:PAYLOAD_SPEC
            ///
            /// where PAYLOADSPEC is a semicolon separated list of specifications of the form
            ///
            ///    OutputName=Prop1.Prop2.PropN
            ///
            /// Into linked list of FilterAndTransform that together forward events from the given
            /// DiagnosticSource's to 'eventSource'. Sets the 'specList' variable to this value
            /// (destroying anything that was there previously).
            ///
            /// By default any serializable properties of the payload object are also included
            /// in the output payload, however this feature and be tuned off by prefixing the
            /// PAYLOADSPEC with a '-'.
            /// </summary>
            public static void CreateFilterAndTransformList(ref FilterAndTransform? specList, string? filterAndPayloadSpecs, DiagnosticSourceEventSource eventSource)
            {
                DestroyFilterAndTransformList(ref specList, eventSource);        // Stop anything that was on before.
                filterAndPayloadSpecs ??= "";
 
                // Points just beyond the last point in the string that has yet to be parsed. Thus we start with the whole string.
                int endIdx = filterAndPayloadSpecs.Length;
                while (true)
                {
                    // Skip trailing whitespace.
                    while (0 < endIdx && char.IsWhiteSpace(filterAndPayloadSpecs[endIdx - 1]))
                        --endIdx;
 
                    int newlineIdx = filterAndPayloadSpecs.LastIndexOf('\n', endIdx - 1, endIdx);
                    int startIdx = 0;
                    if (0 <= newlineIdx)
                        startIdx = newlineIdx + 1;  // starts after the newline, or zero if we don't find one.
 
                    // Skip leading whitespace
                    while (startIdx < endIdx && char.IsWhiteSpace(filterAndPayloadSpecs[startIdx]))
                        startIdx++;
 
                    if (IsActivitySourceEntry(filterAndPayloadSpecs, startIdx, endIdx))
                    {
                        AddNewActivitySourceTransform(filterAndPayloadSpecs, startIdx, endIdx, eventSource);
                    }
                    else
                    {
                        specList = new FilterAndTransform(filterAndPayloadSpecs, startIdx, endIdx, eventSource, specList);
                    }
 
                    endIdx = newlineIdx;
                    if (endIdx < 0)
                        break;
                }
 
                if (eventSource._activitySourceSpecs != null)
                {
                    NormalizeActivitySourceSpecsList(eventSource);
                    CreateActivityListener(eventSource);
                }
            }
 
            /// <summary>
            /// This destroys (turns off) the FilterAndTransform stopping the forwarding started with CreateFilterAndTransformList
            /// </summary>
            /// <param name="specList"></param>
            /// <param name="eventSource"></param>
            public static void DestroyFilterAndTransformList(ref FilterAndTransform? specList, DiagnosticSourceEventSource eventSource)
            {
                eventSource._activityListener?.Dispose();
                eventSource._activityListener = null;
                eventSource._activitySourceSpecs = null; // nothing to dispose inside this list.
 
                var curSpec = specList;
                specList = null;            // Null out the list
                while (curSpec != null)     // Dispose everything in the list.
                {
                    curSpec.Dispose();
                    curSpec = curSpec.Next;
                }
            }
 
            /// <summary>
            /// Creates one FilterAndTransform specification from filterAndPayloadSpec starting at 'startIdx' and ending just before 'endIdx'.
            /// This FilterAndTransform will subscribe to DiagnosticSources specified by the specification and forward them to 'eventSource.
            /// For convenience, the 'Next' field is set to the 'next' parameter, so you can easily form linked lists.
            /// </summary>
            public FilterAndTransform(string filterAndPayloadSpec, int startIdx, int endIdx, DiagnosticSourceEventSource eventSource, FilterAndTransform? next)
            {
                Debug.Assert(filterAndPayloadSpec != null && startIdx >= 0 && startIdx <= endIdx && endIdx <= filterAndPayloadSpec.Length);
                Next = next;
                _eventSource = eventSource;
 
                string? listenerNameFilter = null;       // Means WildCard.
                string? eventNameFilter = null;          // Means WildCard.
                string? activityName = null;
 
                var startTransformIdx = startIdx;
                var endEventNameIdx = endIdx;
                var colonIdx = filterAndPayloadSpec.IndexOf(':', startIdx, endIdx - startIdx);
                if (0 <= colonIdx)
                {
                    endEventNameIdx = colonIdx;
                    startTransformIdx = colonIdx + 1;
                }
 
                // Parse the Source/Event name into listenerNameFilter and eventNameFilter
                var slashIdx = filterAndPayloadSpec.IndexOf('/', startIdx, endEventNameIdx - startIdx);
                if (0 <= slashIdx)
                {
                    listenerNameFilter = filterAndPayloadSpec.Substring(startIdx, slashIdx - startIdx);
 
                    var atIdx = filterAndPayloadSpec.IndexOf('@', slashIdx + 1, endEventNameIdx - slashIdx - 1);
                    if (0 <= atIdx)
                    {
                        activityName = filterAndPayloadSpec.Substring(atIdx + 1, endEventNameIdx - atIdx - 1);
                        eventNameFilter = filterAndPayloadSpec.Substring(slashIdx + 1, atIdx - slashIdx - 1);
                    }
                    else
                    {
                        eventNameFilter = filterAndPayloadSpec.Substring(slashIdx + 1, endEventNameIdx - slashIdx - 1);
                    }
                }
                else if (startIdx < endEventNameIdx)
                {
                    listenerNameFilter = filterAndPayloadSpec.Substring(startIdx, endEventNameIdx - startIdx);
                }
 
                _eventSource.Message("DiagnosticSource: Enabling '" + (listenerNameFilter ?? "*") + "/" + (eventNameFilter ?? "*") + "'");
 
                // If the transform spec begins with a - it means you don't want implicit transforms.
                if (startTransformIdx < endIdx && filterAndPayloadSpec[startTransformIdx] == '-')
                {
                    _eventSource.Message("DiagnosticSource: suppressing implicit transforms.");
                    _noImplicitTransforms = true;
                    startTransformIdx++;
                }
 
                // Parse all the explicit transforms, if present
                if (startTransformIdx < endIdx)
                {
                    while (true)
                    {
                        int specStartIdx = startTransformIdx;
                        int semiColonIdx = filterAndPayloadSpec.LastIndexOf(';', endIdx - 1, endIdx - startTransformIdx);
                        if (0 <= semiColonIdx)
                            specStartIdx = semiColonIdx + 1;
 
                        // Ignore empty specifications.
                        if (specStartIdx < endIdx)
                        {
                            if (_eventSource.IsEnabled(EventLevel.Informational, Keywords.Messages))
                                _eventSource.Message("DiagnosticSource: Parsing Explicit Transform '" + filterAndPayloadSpec.Substring(specStartIdx, endIdx - specStartIdx) + "'");
 
                            _explicitTransforms = new TransformSpec(filterAndPayloadSpec, specStartIdx, endIdx, _explicitTransforms);
                        }
                        if (startTransformIdx == specStartIdx)
                            break;
                        endIdx = semiColonIdx;
                    }
                }
 
                Action<string, string, IEnumerable<KeyValuePair<string, string?>>>? writeEvent = null;
                if (activityName != null && activityName.Contains("Activity"))
                {
                    writeEvent = activityName switch
                    {
                        nameof(Activity1Start) => _eventSource.Activity1Start,
                        nameof(Activity1Stop) => _eventSource.Activity1Stop,
                        nameof(Activity2Start) => _eventSource.Activity2Start,
                        nameof(Activity2Stop) => _eventSource.Activity2Stop,
                        nameof(RecursiveActivity1Start) => _eventSource.RecursiveActivity1Start,
                        nameof(RecursiveActivity1Stop) => _eventSource.RecursiveActivity1Stop,
                        _ => null
                    };
 
                    if (writeEvent == null)
                        _eventSource.Message("DiagnosticSource: Could not find Event to log Activity " + activityName);
                }
 
                writeEvent ??= _eventSource.Event;
 
                // Set up a subscription that watches for the given Diagnostic Sources and events which will call back
                // to the EventSource.
                _diagnosticsListenersSubscription = DiagnosticListener.AllListeners.Subscribe(new CallbackObserver<DiagnosticListener>(delegate (DiagnosticListener newListener)
                {
                    if (listenerNameFilter == null || listenerNameFilter == newListener.Name)
                    {
                        _eventSource.NewDiagnosticListener(newListener.Name);
                        Predicate<string>? eventNameFilterPredicate = null;
                        if (eventNameFilter != null)
                            eventNameFilterPredicate = (string eventName) => eventNameFilter == eventName;
 
                        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
                            Justification = "DiagnosticSource.Write is marked with RequiresUnreferencedCode.")]
                        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2119",
                            Justification = "DAM on EventSource references this compiler-generated local function which calls a " +
                                            "method that requires unreferenced code. EventSource will not access this local function.")]
                        void OnEventWritten(KeyValuePair<string, object?> evnt)
                        {
                            // The filter given to the DiagnosticSource may not work if users don't is 'IsEnabled' as expected.
                            // Thus we look for any events that may have snuck through and filter them out before forwarding.
                            if (eventNameFilter != null && eventNameFilter != evnt.Key)
                                return;
 
                            var outputArgs = this.Morph(evnt.Value);
                            var eventName = evnt.Key;
                            writeEvent(newListener.Name, eventName, outputArgs);
                        }
 
                        var subscription = newListener.Subscribe(new CallbackObserver<KeyValuePair<string, object?>>(OnEventWritten), eventNameFilterPredicate);
                        _liveSubscriptions = new Subscriptions(subscription, _liveSubscriptions);
                    }
                }));
            }
 
            internal FilterAndTransform(string filterAndPayloadSpec, int endIdx, int colonIdx, string activitySourceName, string? activityName, ActivityEvents events, ActivitySamplingResult samplingResult, DiagnosticSourceEventSource eventSource)
            {
                _eventSource = eventSource;
 
                Next = _eventSource._activitySourceSpecs;
                _eventSource._activitySourceSpecs = this;
 
                SourceName = activitySourceName;
                ActivityName = activityName;
                Events = events;
                SamplingResult = samplingResult;
 
                if (colonIdx >= 0)
                {
                    int startTransformIdx = colonIdx + 1;
 
                    // If the transform spec begins with a - it means you don't want implicit transforms.
                    if (startTransformIdx < endIdx && filterAndPayloadSpec[startTransformIdx] == '-')
                    {
                        _eventSource.Message("DiagnosticSource: suppressing implicit transforms.");
                        _noImplicitTransforms = true;
                        startTransformIdx++;
                    }
 
                    // Parse all the explicit transforms, if present
                    if (startTransformIdx < endIdx)
                    {
                        while (true)
                        {
                            int specStartIdx = startTransformIdx;
                            int semiColonIdx = filterAndPayloadSpec.LastIndexOf(';', endIdx - 1, endIdx - startTransformIdx);
                            if (0 <= semiColonIdx)
                                specStartIdx = semiColonIdx + 1;
 
                            // Ignore empty specifications.
                            if (specStartIdx < endIdx)
                            {
                                if (_eventSource.IsEnabled(EventLevel.Informational, Keywords.Messages))
                                    _eventSource.Message("DiagnosticSource: Parsing Explicit Transform '" + filterAndPayloadSpec.Substring(specStartIdx, endIdx - specStartIdx) + "'");
 
                                _explicitTransforms = new TransformSpec(filterAndPayloadSpec, specStartIdx, endIdx, _explicitTransforms);
                            }
                            if (startTransformIdx == specStartIdx)
                                break;
                            endIdx = semiColonIdx;
                        }
                    }
                }
            }
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            internal static bool IsActivitySourceEntry(string filterAndPayloadSpec, int startIdx, int endIdx) =>
                            filterAndPayloadSpec.AsSpan(startIdx, endIdx - startIdx).StartsWith(c_ActivitySourcePrefix.AsSpan(), StringComparison.Ordinal);
 
            internal static void AddNewActivitySourceTransform(string filterAndPayloadSpec, int startIdx, int endIdx, DiagnosticSourceEventSource eventSource)
            {
                Debug.Assert(endIdx - startIdx >= 4);
                Debug.Assert(IsActivitySourceEntry(filterAndPayloadSpec, startIdx, endIdx));
 
                ReadOnlySpan<char> eventName;
                ReadOnlySpan<char> activitySourceName;
 
                ActivityEvents supportedEvent = ActivityEvents.All; // Default events
                ActivitySamplingResult samplingResult = ActivitySamplingResult.AllDataAndRecorded; // Default sampling results
 
                int colonIdx = filterAndPayloadSpec.IndexOf(':', startIdx + c_ActivitySourcePrefix.Length, endIdx - startIdx - c_ActivitySourcePrefix.Length);
 
                ReadOnlySpan<char> entry = filterAndPayloadSpec.AsSpan(
                                                startIdx + c_ActivitySourcePrefix.Length,
                                                (colonIdx >= 0 ? colonIdx : endIdx) - startIdx - c_ActivitySourcePrefix.Length)
                                                .Trim();
 
                int eventNameIndex = entry.IndexOf('/');
                if (eventNameIndex >= 0)
                {
                    activitySourceName = entry.Slice(0, eventNameIndex).Trim();
 
                    ReadOnlySpan<char> suffixPart = entry.Slice(eventNameIndex + 1).Trim();
                    int samplingResultIndex = suffixPart.IndexOf('-');
                    if (samplingResultIndex >= 0)
                    {
                        // We have the format "[AS]SourceName/[EventName]-[SamplingResult]
                        eventName = suffixPart.Slice(0, samplingResultIndex).Trim();
                        suffixPart = suffixPart.Slice(samplingResultIndex + 1).Trim();
 
                        if (suffixPart.Length > 0)
                        {
                            if (suffixPart.Equals("Propagate".AsSpan(), StringComparison.OrdinalIgnoreCase))
                            {
                                samplingResult = ActivitySamplingResult.PropagationData;
                            }
                            else if (suffixPart.Equals("Record".AsSpan(), StringComparison.OrdinalIgnoreCase))
                            {
                                samplingResult = ActivitySamplingResult.AllData;
                            }
                            else
                            {
                                // Invalid format
                                return;
                            }
                        }
                    }
                    else
                    {
                        // We have the format "[AS]SourceName/[EventName]
                        eventName = suffixPart;
                    }
 
                    if (eventName.Length > 0)
                    {
                        if (eventName.Equals("Start".AsSpan(), StringComparison.OrdinalIgnoreCase))
                        {
                            supportedEvent = ActivityEvents.ActivityStart;
                        }
                        else if (eventName.Equals("Stop".AsSpan(), StringComparison.OrdinalIgnoreCase))
                        {
                            supportedEvent = ActivityEvents.ActivityStop;
                        }
                        else
                        {
                            // Invalid format
                            return;
                        }
                    }
                }
                else
                {
                    // We have the format "[AS]SourceName"
                    activitySourceName = entry;
                }
 
                string? activityName = null;
                int plusSignIndex = activitySourceName.IndexOf('+');
                if (plusSignIndex >= 0)
                {
                    activityName = activitySourceName.Slice(plusSignIndex + 1).Trim().ToString();
                    activitySourceName = activitySourceName.Slice(0, plusSignIndex).Trim();
                }
 
                new FilterAndTransform(filterAndPayloadSpec, endIdx, colonIdx, activitySourceName.ToString(), activityName, supportedEvent, samplingResult, eventSource);
            }
 
            // Check if we are interested to listen to such ActivitySource
            private static ActivitySamplingResult Sample(string activitySourceName, string activityName, DiagnosticSourceEventSource eventSource)
            {
                FilterAndTransform? list = eventSource._activitySourceSpecs;
                ActivitySamplingResult specificResult = ActivitySamplingResult.None;
                ActivitySamplingResult wildResult = ActivitySamplingResult.None;
 
                while (list != null)
                {
                    if (list.ActivityName == null || list.ActivityName == activityName)
                    {
                        if (activitySourceName == list.SourceName)
                        {
                                if (list.SamplingResult > specificResult)
                                {
                                    specificResult = list.SamplingResult;
                                }
 
                                if (specificResult >= ActivitySamplingResult.AllDataAndRecorded)
                                {
                                    return specificResult; // highest possible value
                                }
                                // We don't break here as we can have more than one entry with the same source name.
                            }
                        else if (list.SourceName == "*")
                        {
                            if (specificResult != ActivitySamplingResult.None)
                            {
                                // We reached the '*' nodes which means there is no more specific source names in the list.
                                // If we encountered any specific node before, then return that value.
                                return specificResult;
                            }
 
                            if (list.SamplingResult > wildResult)
                            {
                                wildResult = list.SamplingResult;
                            }
                        }
                    }
                    list = list.Next;
                }
 
                // We can return None in case there is no '*' nor any entry match the source name.
                return specificResult != ActivitySamplingResult.None ? specificResult : wildResult;
            }
 
            internal static void CreateActivityListener(DiagnosticSourceEventSource eventSource)
            {
                Debug.Assert(eventSource._activityListener == null);
                Debug.Assert(eventSource._activitySourceSpecs != null);
 
                eventSource._activityListener = new ActivityListener();
 
                eventSource._activityListener.SampleUsingParentId = (ref ActivityCreationOptions<string> activityOptions) => Sample(activityOptions.Source.Name, activityOptions.Name, eventSource);
                eventSource._activityListener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) => Sample(activityOptions.Source.Name, activityOptions.Name, eventSource);
 
                eventSource._activityListener.ShouldListenTo = (activitySource) =>
                {
                    FilterAndTransform? list = eventSource._activitySourceSpecs;
                    while (list != null)
                    {
                        if (activitySource.Name == list.SourceName || list.SourceName == "*")
                        {
                            return true;
                        }
 
                        list = list.Next;
                    }
 
                    return false;
                };
 
                eventSource._activityListener.ActivityStarted = activity => OnActivityStarted(eventSource, activity);
 
                eventSource._activityListener.ActivityStopped = activity => OnActivityStopped(eventSource, activity);
 
                ActivitySource.AddActivityListener(eventSource._activityListener);
            }
 
            [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Activity))]
            [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityContext))]
            [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityEvent))]
            [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityLink))]
            [DynamicDependency(nameof(DateTime.Ticks), typeof(DateTime))]
            [DynamicDependency(nameof(TimeSpan.Ticks), typeof(TimeSpan))]
            [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
                Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")]
            private static void OnActivityStarted(DiagnosticSourceEventSource eventSource, Activity activity)
            {
                FilterAndTransform? list = eventSource._activitySourceSpecs;
                while (list != null)
                {
                    if ((list.Events & ActivityEvents.ActivityStart) != 0 &&
                        (activity.Source.Name == list.SourceName || list.SourceName == "*") &&
                        (list.ActivityName == null || list.ActivityName == activity.OperationName))
                    {
                        eventSource.ActivityStart(activity.Source.Name, activity.OperationName, list.Morph(activity));
                        return;
                    }
 
                    list = list.Next;
                }
            }
 
            [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
                Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")]
            private static void OnActivityStopped(DiagnosticSourceEventSource eventSource, Activity activity)
            {
                FilterAndTransform? list = eventSource._activitySourceSpecs;
                while (list != null)
                {
                    if ((list.Events & ActivityEvents.ActivityStop) != 0 &&
                        (activity.Source.Name == list.SourceName || list.SourceName == "*") &&
                        (list.ActivityName == null || list.ActivityName == activity.OperationName))
                    {
                        eventSource.ActivityStop(activity.Source.Name, activity.OperationName, list.Morph(activity));
                        return;
                    }
 
                    list = list.Next;
                }
            }
 
            // Move all wildcard nodes at the end of the list.
            // This will give more priority to the specific nodes over the wildcards.
            internal static void NormalizeActivitySourceSpecsList(DiagnosticSourceEventSource eventSource)
            {
                Debug.Assert(eventSource._activityListener == null);
                Debug.Assert(eventSource._activitySourceSpecs != null);
 
                FilterAndTransform? list = eventSource._activitySourceSpecs;
 
                FilterAndTransform? firstSpecificList = null;
                FilterAndTransform? lastSpecificList = null;
 
                FilterAndTransform? firstWildcardList = null;
                FilterAndTransform? lastWildcardList = null;
 
                while (list != null)
                {
                    if (list.SourceName == "*")
                    {
                        if (firstWildcardList == null)
                        {
                            firstWildcardList = lastWildcardList = list;
                        }
                        else
                        {
                            Debug.Assert(lastWildcardList != null);
                            lastWildcardList.Next = list;
                            lastWildcardList = list;
                        }
                    }
                    else
                    {
                        if (firstSpecificList == null)
                        {
                            firstSpecificList = lastSpecificList = list;
                        }
                        else
                        {
                            Debug.Assert(lastSpecificList != null);
                            lastSpecificList.Next = list;
                            lastSpecificList = list;
                        }
                    }
 
                    list = list.Next;
                }
 
                if (firstSpecificList == null || firstWildcardList == null)
                {
                    Debug.Assert(firstSpecificList != null || firstWildcardList != null);
                    return; // list shouldn't be chanaged.
                }
 
                Debug.Assert(lastWildcardList != null && lastSpecificList != null);
 
                lastSpecificList.Next = firstWildcardList;
                lastWildcardList.Next = null;
 
                eventSource._activitySourceSpecs = firstSpecificList;
            }
 
            private void Dispose()
            {
                if (_diagnosticsListenersSubscription != null)
                {
                    _diagnosticsListenersSubscription.Dispose();
                    _diagnosticsListenersSubscription = null;
                }
 
                if (_liveSubscriptions != null)
                {
                    Subscriptions? subscr = _liveSubscriptions;
                    _liveSubscriptions = null;
                    while (subscr != null)
                    {
                        subscr.Subscription.Dispose();
                        subscr = subscr.Next;
                    }
                }
            }
 
            [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2112:ReflectionToRequiresUnreferencedCode",
                Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves this method which " +
                                "requires unreferenced code, but EnsureDescriptorsInitialized does not access this member and is safe to call.")]
            [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
            public List<KeyValuePair<string, string?>> Morph(object? args)
            {
                // Transform the args into a bag of key-value strings.
                var outputArgs = new List<KeyValuePair<string, string?>>();
                if (args != null)
                {
                    if (!_noImplicitTransforms)
                    {
                        // given the type, fetch the implicit transforms for that type and put it in the implicitTransforms variable.
                        Type argType = args.GetType();
                        TransformSpec? implicitTransforms;
 
                        // First check the one-element cache _firstImplicitTransformsEntry
                        ImplicitTransformEntry? cacheEntry = _firstImplicitTransformsEntry;
                        if (cacheEntry != null && cacheEntry.Type == argType)
                        {
                            implicitTransforms = cacheEntry.Transforms;     // Yeah we hit the cache.
                        }
                        else if (cacheEntry == null)
                        {
                            // _firstImplicitTransformsEntry is empty, we should fill it.
                            // Note that it is OK that two threads may race and both call MakeImplicitTransforms on their own
                            // (that is we don't expect exactly once initialization of _firstImplicitTransformsEntry)
                            implicitTransforms = MakeImplicitTransforms(argType);
                            Interlocked.CompareExchange(ref _firstImplicitTransformsEntry,
                                new ImplicitTransformEntry() { Type = argType, Transforms = implicitTransforms }, null);
                        }
                        else
                        {
                            // This should only happen when you are wildcarding your events (reasonably rare).
                            // In that case you will probably need many types
                            // Note currently we don't limit the cache size, but it is limited by the number of
                            // distinct types of objects passed to DiagnosticSource.Write.
                            if (_implicitTransformsTable == null)
                            {
                                Interlocked.CompareExchange(ref _implicitTransformsTable,
                                    new ConcurrentDictionary<Type, TransformSpec?>(1, 8), null);
                            }
                            implicitTransforms = _implicitTransformsTable.GetOrAdd(argType, MakeImplicitTransforms);
                        }
 
                        // implicitTransformas now fetched from cache or constructed, use it to Fetch all the implicit fields.
                        if (implicitTransforms != null)
                        {
                            for (TransformSpec? serializableArg = implicitTransforms; serializableArg != null; serializableArg = serializableArg.Next)
                                outputArgs.Add(serializableArg.Morph(args));
                        }
                    }
 
                    if (_explicitTransforms != null)
                    {
                        for (TransformSpec? explicitTransform = _explicitTransforms; explicitTransform != null; explicitTransform = explicitTransform.Next)
                        {
                            var keyValue = explicitTransform.Morph(args);
                            if (keyValue.Value != null)
                                outputArgs.Add(keyValue);
                        }
                    }
                }
                return outputArgs;
            }
 
            public FilterAndTransform? Next;
 
            // Specific ActivitySource Transforms information
 
            internal const string c_ActivitySourcePrefix = "[AS]";
            internal string? SourceName { get; set; }
            internal string? ActivityName { get; set; }
            internal DiagnosticSourceEventSource.ActivityEvents Events  { get; set; }
            internal ActivitySamplingResult SamplingResult { get; set; }
 
            #region private
 
            // Given a type generate all the implicit transforms for type (that is for every field
            // generate the spec that fetches it).
            [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2112:ReflectionToRequiresUnreferencedCode",
                Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves this method which " +
                                "requires unreferenced code, but EnsureDescriptorsInitialized does not access this member and is safe to call.")]
            [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
            private static TransformSpec? MakeImplicitTransforms(Type type)
            {
                TransformSpec? newSerializableArgs = null;
                TypeInfo curTypeInfo = type.GetTypeInfo();
                foreach (PropertyInfo property in curTypeInfo.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    // prevent TransformSpec from attempting to implicitly transform index properties
                    if (property.GetMethod == null || property.GetMethod!.GetParameters().Length > 0)
                        continue;
                    newSerializableArgs = new TransformSpec(property.Name, 0, property.Name.Length, newSerializableArgs);
                }
                return Reverse(newSerializableArgs);
            }
 
            // Reverses a linked list (of TransformSpecs) in place.
            private static TransformSpec? Reverse(TransformSpec? list)
            {
                TransformSpec? ret = null;
                while (list != null)
                {
                    var next = list.Next;
                    list.Next = ret;
                    ret = list;
                    list = next;
                }
                return ret;
            }
 
            private IDisposable? _diagnosticsListenersSubscription; // This is our subscription that listens for new Diagnostic source to appear.
            private Subscriptions? _liveSubscriptions;              // These are the subscriptions that we are currently forwarding to the EventSource.
            private readonly bool _noImplicitTransforms;                    // Listener can say they don't want implicit transforms.
            private ImplicitTransformEntry? _firstImplicitTransformsEntry; // The transform for _firstImplicitFieldsType
            private ConcurrentDictionary<Type, TransformSpec?>? _implicitTransformsTable; // If there is more than one object type for an implicit transform, they go here.
            private readonly TransformSpec? _explicitTransforms;             // payload to include because the user explicitly indicated how to fetch the field.
            private readonly DiagnosticSourceEventSource _eventSource;      // Where the data is written to.
            #endregion
        }
 
        // This olds one the implicit transform for one type of object.
        // We remember this type-transform pair in the _firstImplicitTransformsEntry cache.
        internal sealed class ImplicitTransformEntry
        {
            public Type? Type;
            public TransformSpec? Transforms;
        }
 
        /// <summary>
        /// Transform spec represents a string that describes how to extract a piece of data from
        /// the DiagnosticSource payload. An example string is OUTSTR=EVENT_VALUE.PROP1.PROP2.PROP3
        /// It has a Next field so they can be chained together in a linked list.
        /// </summary>
        internal sealed class TransformSpec
        {
            /// <summary>
            /// parse the strings 'spec' from startIdx to endIdx (points just beyond the last considered char)
            /// The syntax is ID1=ID2.ID3.ID4 .... Where ID1= is optional.
            /// </summary>
            public TransformSpec(string transformSpec, int startIdx, int endIdx, TransformSpec? next = null)
            {
                Debug.Assert(transformSpec != null && startIdx >= 0 && startIdx < endIdx && endIdx <= transformSpec.Length);
                Next = next;
 
                // Pick off the Var=
                int equalsIdx = transformSpec.IndexOf('=', startIdx, endIdx - startIdx);
                if (0 <= equalsIdx)
                {
                    _outputName = transformSpec.Substring(startIdx, equalsIdx - startIdx);
                    startIdx = equalsIdx + 1;
                }
 
                // Working from back to front, create a PropertySpec for each .ID in the string.
                while (startIdx < endIdx)
                {
                    int dotIdx = transformSpec.LastIndexOf('.', endIdx - 1, endIdx - startIdx);
                    int idIdx = startIdx;
                    if (0 <= dotIdx)
                        idIdx = dotIdx + 1;
 
                    string propertyName = transformSpec.Substring(idIdx, endIdx - idIdx);
                    _fetches = new PropertySpec(propertyName, _fetches);
 
                    // If the user did not explicitly set a name, it is the last one (first to be processed from the end).
                    _outputName ??= propertyName;
 
                    endIdx = dotIdx;    // This works even when LastIndexOf return -1.
                }
            }
 
            /// <summary>
            /// Given the DiagnosticSourcePayload 'obj', compute a key-value pair from it. For example
            /// if the spec is OUTSTR=EVENT_VALUE.PROP1.PROP2.PROP3 and the ultimate value of PROP3 is
            /// 10 then the return key value pair is  KeyValuePair("OUTSTR","10")
            /// </summary>
            [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2112:ReflectionToRequiresUnreferencedCode",
                Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves this method which " +
                                "requires unreferenced code, but EnsureDescriptorsInitialized does not access this member and is safe to call.")]
            [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
            public KeyValuePair<string, string?> Morph(object? obj)
            {
                for (PropertySpec? cur = _fetches; cur != null; cur = cur.Next)
                {
                    if (obj != null || cur.IsStatic)
                        obj = cur.Fetch(obj);
                }
 
                return new KeyValuePair<string, string?>(_outputName, obj?.ToString());
            }
 
            /// <summary>
            /// A public field that can be used to form a linked list.
            /// </summary>
            public TransformSpec? Next;
 
            #region private
            /// <summary>
            /// A PropertySpec represents information needed to fetch a property from
            /// and efficiently. Thus it represents a '.PROP' in a TransformSpec
            /// (and a transformSpec has a list of these).
            /// </summary>
            internal sealed class PropertySpec
            {
                private const string CurrentActivityPropertyName = "*Activity";
                private const string EnumeratePropertyName = "*Enumerate";
 
                /// <summary>
                /// Make a new PropertySpec for a property named 'propertyName'.
                /// For convenience you can set he 'next' field to form a linked
                /// list of PropertySpecs.
                /// </summary>
                public PropertySpec(string propertyName, PropertySpec? next)
                {
                    Next = next;
                    _propertyName = propertyName;
 
                    // detect well-known names that are static functions
                    if (_propertyName == CurrentActivityPropertyName)
                    {
                        IsStatic = true;
                    }
                }
 
                public bool IsStatic { get; }
 
                /// <summary>
                /// Given an object fetch the property that this PropertySpec represents.
                /// obj may be null when IsStatic is true, otherwise it must be non-null.
                /// </summary>
                [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2112:ReflectionToRequiresUnreferencedCode",
                    Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves this method which " +
                                    "requires unreferenced code, but EnsureDescriptorsInitialized does not access this member and is safe to call.")]
                [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
                public object? Fetch(object? obj)
                {
                    PropertyFetch? fetch = _fetchForExpectedType;
                    Debug.Assert(obj != null || IsStatic);
                    Type? objType = obj?.GetType();
                    if (fetch == null || fetch.Type != objType)
                    {
                        _fetchForExpectedType = fetch = PropertyFetch.FetcherForProperty(objType, _propertyName);
                    }
                    object? ret = null;
                    // Avoid the exception which can be thrown during accessing the object properties.
                    try { ret = fetch!.Fetch(obj); } catch (Exception e) { Log.Message($"Property {objType}.{_propertyName} threw the exception {e}"); }
                    return ret;
                }
 
                /// <summary>
                /// A public field that can be used to form a linked list.
                /// </summary>
                public PropertySpec? Next;
 
                #region private
                /// <summary>
                /// PropertyFetch is a helper class. It takes a PropertyInfo and then knows how
                /// to efficiently fetch that property from a .NET object (See Fetch method).
                /// It hides some slightly complex generic code.
                /// </summary>
                private class PropertyFetch
                {
                    public PropertyFetch(Type? type)
                    {
                        Type = type;
                    }
 
                    /// <summary>
                    /// The type of the object that the property is fetched from. For well-known static methods that
                    /// aren't actually property getters this will return null.
                    /// </summary>
                    internal Type? Type { get; }
 
                    /// <summary>
                    /// Create a property fetcher for a propertyName
                    /// </summary>
                    [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2112:ReflectionToRequiresUnreferencedCode",
                        Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves this method which " +
                                        "requires unreferenced code, but EnsureDescriptorsInitialized does not access this member and is safe to call.")]
                    [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)]
                    public static PropertyFetch FetcherForProperty(Type? type, string propertyName)
                    {
                        if (propertyName == null)
                            return new PropertyFetch(type);     // returns null on any fetch.
                        if (propertyName == CurrentActivityPropertyName)
                        {
                            return new CurrentActivityPropertyFetch();
                        }
 
                        Debug.Assert(type != null, "Type should only be null for the well-known static fetchers already checked");
                        TypeInfo typeInfo = type.GetTypeInfo();
                        if (propertyName == EnumeratePropertyName)
                        {
                            // If there are multiple implementations of IEnumerable<T>, this arbitrarily uses the first one
                            foreach (Type iFaceType in typeInfo.GetInterfaces())
                            {
                                TypeInfo iFaceTypeInfo = iFaceType.GetTypeInfo();
                                if (!iFaceTypeInfo.IsGenericType ||
                                    iFaceTypeInfo.GetGenericTypeDefinition() != typeof(IEnumerable<>))
                                {
                                    continue;
                                }
 
                                return CreateEnumeratePropertyFetch(type, iFaceTypeInfo);
                            }
 
                            // no implementation of IEnumerable<T> found, return a null fetcher
                            Log.Message($"*Enumerate applied to non-enumerable type {type}");
                            return new PropertyFetch(type);
                        }
                        else
                        {
                            PropertyInfo? propertyInfo = typeInfo.GetDeclaredProperty(propertyName);
                            if (propertyInfo == null)
                            {
                                foreach (PropertyInfo pi in typeInfo.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
                                {
                                    if (pi.Name == propertyName)
                                    {
                                        propertyInfo = pi;
                                        break;
                                    }
                                }
                            }
 
                            if (propertyInfo == null)
                            {
                                Log.Message($"Property {propertyName} not found on {type}. Ensure the name is spelled correctly. If you published the application with PublishTrimmed=true, ensure the property was not trimmed away.");
                                return new PropertyFetch(type);
                            }
                            // Delegate creation below is incompatible with static properties.
                            else if (propertyInfo.GetMethod?.IsStatic == true || propertyInfo.SetMethod?.IsStatic == true)
                            {
                                Log.Message($"Property {propertyName} is static.");
                                return new PropertyFetch(type);
                            }
 
                            return CreatePropertyFetch(typeInfo, propertyInfo);
                        }
                    }
 
                    [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode",
                        Justification = "MakeGenericType is only called when IsDynamicCodeSupported is true or only with ref types.")]
                    private static PropertyFetch CreateEnumeratePropertyFetch(Type type, TypeInfo enumerableOfTType)
                    {
                        Type elemType = enumerableOfTType.GetGenericArguments()[0];
#if NET
                        if (!RuntimeFeature.IsDynamicCodeSupported && elemType.IsValueType)
                        {
                            return new EnumeratePropertyFetch(type);
                        }
#endif
                        Type instantiatedTypedPropertyFetcher = typeof(EnumeratePropertyFetch<>)
                            .GetTypeInfo().MakeGenericType(elemType);
                        return (PropertyFetch)Activator.CreateInstance(instantiatedTypedPropertyFetcher, type)!;
                    }
 
                    [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode",
                        Justification = "MakeGenericType is only called when IsDynamicCodeSupported is true or only with ref types.")]
                    private static PropertyFetch CreatePropertyFetch(Type type, PropertyInfo propertyInfo)
                    {
#if NET
                        if (!RuntimeFeature.IsDynamicCodeSupported && (propertyInfo.DeclaringType!.IsValueType || propertyInfo.PropertyType.IsValueType))
                        {
                            return new ReflectionPropertyFetch(type, propertyInfo);
                        }
#endif
                        Type typedPropertyFetcher = type.IsValueType ?
                            typeof(ValueTypedFetchProperty<,>) : typeof(RefTypedFetchProperty<,>);
                        Type instantiatedTypedPropertyFetcher = typedPropertyFetcher.GetTypeInfo().MakeGenericType(
                            propertyInfo.DeclaringType!, propertyInfo.PropertyType);
                        return (PropertyFetch)Activator.CreateInstance(instantiatedTypedPropertyFetcher, type, propertyInfo)!;
                    }
 
                    /// <summary>
                    /// Given an object, fetch the property that this propertyFech represents.
                    /// </summary>
                    public virtual object? Fetch(object? obj) { return null; }
 
#region private
 
                    private sealed class RefTypedFetchProperty<TObject, TProperty> : PropertyFetch
                    {
                        public RefTypedFetchProperty(Type type, PropertyInfo property) : base(type)
                        {
                            Debug.Assert(typeof(TObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()));
                            _propertyFetch = (Func<TObject, TProperty>)property.GetMethod!.CreateDelegate(typeof(Func<TObject, TProperty>));
                        }
                        public override object? Fetch(object? obj)
                        {
                            Debug.Assert(obj is TObject);
                            return _propertyFetch((TObject)obj);
                        }
                        private readonly Func<TObject, TProperty> _propertyFetch;
                    }
 
                    private delegate TProperty StructFunc<TStruct, TProperty>(ref TStruct thisArg);
 
                    // Value types methods require that the first argument is passed by reference. This requires a different delegate signature
                    // from the reference type case.
                    private sealed class ValueTypedFetchProperty<TStruct, TProperty> : PropertyFetch
                    {
                        public ValueTypedFetchProperty(Type type, PropertyInfo property) : base(type)
                        {
                            Debug.Assert(typeof(TStruct) == type);
                            _propertyFetch = (StructFunc<TStruct, TProperty>)property.GetMethod!.CreateDelegate(typeof(StructFunc<TStruct, TProperty>));
                        }
                        public override object? Fetch(object? obj)
                        {
                            Debug.Assert(obj is TStruct);
                            // It is uncommon for property getters to mutate the struct, but if they do the change will be lost.
                            // We are calling the getter on an unboxed copy
                            TStruct structObj = (TStruct)obj;
                            return _propertyFetch(ref structObj);
                        }
                        private readonly StructFunc<TStruct, TProperty> _propertyFetch;
                    }
 
#if NET
                    /// <summary>
                    /// A fetcher that can be used when MakeGenericType isn't available.
                    /// </summary>
                    private sealed class ReflectionPropertyFetch : PropertyFetch
                    {
                        private readonly MethodInvoker _getterInvoker;
                        public ReflectionPropertyFetch(Type type, PropertyInfo property) : base(type)
                        {
                            _getterInvoker = MethodInvoker.Create(property.GetMethod!);
                        }
 
                        public override object? Fetch(object? obj) => _getterInvoker.Invoke(obj);
                    }
 
                    /// <summary>
                    /// A fetcher that enumerates and formats an IEnumerable when MakeGenericType isn't available.
                    /// </summary>
                    private sealed class EnumeratePropertyFetch : PropertyFetch
                    {
                        public EnumeratePropertyFetch(Type type) : base(type) { }
 
                        public override object? Fetch(object? obj)
                        {
                            IEnumerable? enumerable = obj as IEnumerable;
                            Debug.Assert(enumerable is not null);
 
                            // string.Join for a non-generic IEnumerable
                            IEnumerator en = enumerable.GetEnumerator();
                            using (IDisposable? disposable = en as IDisposable)
                            {
                                if (!en.MoveNext())
                                {
                                    return string.Empty;
                                }
 
                                object? currentValue = en.Current;
                                string? firstString = currentValue?.ToString();
 
                                // If there's only 1 item, simply return the ToString of that
                                if (!en.MoveNext())
                                {
                                    // Only one value available
                                    return firstString ?? string.Empty;
                                }
 
                                var result = new ValueStringBuilder(stackalloc char[256]);
 
                                result.Append(firstString);
 
                                do
                                {
                                    currentValue = en.Current;
 
                                    result.Append(",");
                                    if (currentValue != null)
                                    {
                                        result.Append(currentValue.ToString());
                                    }
                                }
                                while (en.MoveNext());
 
                                return result.ToString();
                            }
                        }
                    }
#endif
 
                    /// <summary>
                    /// A fetcher that returns the result of Activity.Current
                    /// </summary>
                    private sealed class CurrentActivityPropertyFetch : PropertyFetch
                    {
                        public CurrentActivityPropertyFetch() : base(null) { }
                        public override object? Fetch(object? obj)
                        {
                            return Activity.Current;
                        }
                    }
 
                    /// <summary>
                    /// A fetcher that enumerates and formats an IEnumerable
                    /// </summary>
                    private sealed class EnumeratePropertyFetch<ElementType> : PropertyFetch
                    {
                        public EnumeratePropertyFetch(Type type) : base(type) { }
                        public override object? Fetch(object? obj)
                        {
                            Debug.Assert(obj is IEnumerable<ElementType>);
                            return string.Join(",", (IEnumerable<ElementType>)obj);
                        }
                    }
#endregion
                }
 
                private readonly string _propertyName;
                private volatile PropertyFetch? _fetchForExpectedType;
#endregion
            }
 
            private readonly string _outputName = null!;
            private readonly PropertySpec? _fetches;
#endregion
        }
 
        /// <summary>
        /// CallbackObserver is an adapter class that creates an observer (which you can pass
        /// to IObservable.Subscribe), and calls the given callback every time the 'next'
        /// operation on the IObserver happens.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        internal sealed class CallbackObserver<T> : IObserver<T>
        {
            public CallbackObserver(Action<T> callback) { _callback = callback; }
 
#region private
            public void OnCompleted() { }
            public void OnError(Exception error) { }
            public void OnNext(T value) { _callback(value); }
 
            private readonly Action<T> _callback;
#endregion
        }
 
        // A linked list of IObservable subscriptions (which are IDisposable).
        // We use this to keep track of the DiagnosticSource subscriptions.
        // We use this linked list for thread atomicity
        internal sealed class Subscriptions
        {
            public Subscriptions(IDisposable subscription, Subscriptions? next)
            {
                Subscription = subscription;
                Next = next;
            }
            public IDisposable Subscription;
            public Subscriptions? Next;
        }
 
#endregion
 
        private FilterAndTransform? _specs;                 // Transformation specifications that indicate which sources/events are forwarded.
        private FilterAndTransform? _activitySourceSpecs;   // ActivitySource Transformation specifications that indicate which sources/events are forwarded.
        private ActivityListener? _activityListener;
#endregion
    }
}