/// <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);        // Stop anything that was on before.
                if (filterAndPayloadSpecs == null)
                {
                    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;

                for (;;)
                {
                    // 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++;
                    }

                    specList = new FilterAndTransform(filterAndPayloadSpecs, startIdx, endIdx, eventSource, specList);
                    endIdx   = newlineIdx;
                    if (endIdx < 0)
                    {
                        break;
                    }
                }
            }
            /// <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)
                {
                    for (;;)
                    {
                        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"))
                {
                    MethodInfo writeEventMethodInfo = typeof(DiagnosticSourceEventSource).GetTypeInfo().GetDeclaredMethod(activityName);
                    if (writeEventMethodInfo != null)
                    {
                        // This looks up the activityName (which needs to be a name of an event on DiagnosticSourceEventSource
                        // like Activity1Start and returns that method).   This allows us to have a number of them and this code
                        // just works.
                        try
                        {
                            writeEvent = (Action <string, string, IEnumerable <KeyValuePair <string, string> > >)
                                         writeEventMethodInfo.CreateDelegate(typeof(Action <string, string, IEnumerable <KeyValuePair <string, string> > >), _eventSource);
                        }
                        catch (Exception) { }
                    }
                    if (writeEvent == null)
                    {
                        _eventSource.Message("DiagnosticSource: Could not find Event to log Activity " + activityName);
                    }
                }

                if (writeEvent == null)
                {
#if !NO_EVENTSOURCE_COMPLEX_TYPE_SUPPORT
                    writeEvent = _eventSource.Event;
#else
                    writeEvent = delegate(string sourceName, string eventName, IEnumerable <KeyValuePair <string, string> > arguments)
                    {
                        _eventSource.EventJson(sourceName, eventName, ToJson(arguments));
                    };
#endif
                }

                // 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;
                        }

                        var subscription = newListener.Subscribe(new CallbackObserver <KeyValuePair <string, object> >(delegate(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);
                        }), eventNameFilterPredicate);
                        _liveSubscriptions = new Subscriptions(subscription, _liveSubscriptions);
                    }
                }));
            }
Beispiel #3
0
            /// <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)
            {
#if DEBUG
                string spec = filterAndPayloadSpec.Substring(startIdx, endIdx - startIdx);
#endif
                Next         = next;
                _eventSource = eventSource;

                string listenerNameFilter = null;       // Means WildCard.
                string eventNameFilter    = null;       // Means WildCard.

                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);
                    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)
                {
                    for (;;)
                    {
                        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;
                    }
                }

                // 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)
                    {
                        Predicate <string> eventNameFilterPredicate = null;
                        if (eventNameFilter != null)
                        {
                            eventNameFilterPredicate = (string eventName) => eventNameFilter == eventName;
                        }

                        var subscription = newListener.Subscribe(new CallbackObserver <KeyValuePair <string, object> >(delegate(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);
#if !NO_EVENTSOURCE_COMPLEX_TYPE_SUPPORT
                            _eventSource.Event(newListener.Name, evnt.Key, outputArgs);
#else
                            _eventSource.EventJson(newListener.Name, evnt.Key, ToJson(outputArgs));
#endif
                        }), eventNameFilterPredicate);
                        _liveSubscriptions = new Subscriptions(subscription, _liveSubscriptions);
                    }
                }));
            }