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