void ProcessDiagnosticEventInternal(IDiagnosticEvent de, DiagnosticsConfiguration config_ref) { // NOTE: This method is *performance critical* and should be optimized for quickest execution even at cost of readability try { if (!config_ref.Settings.EnableLogging) return; //# Include Caller Info if (config_ref.Settings.IncludeCallerInfo || config_ref.Settings.UseCallerNameAsLoggerName) { de.IncludeCallerInformation( config_ref.Settings.UseCallerNameAsLoggerName, config_ref.Settings.IncludeCallerInfo); } //# Override logger name only if it has not already been set (custom name might have been used) if (de.LoggerName == null) de.LoggerName = Name.ToString(); //# COPY GLOBAL PROPERTIES de.Properties.AddOrUpdateRange(GlobalProperties); //# EVALUATE AND PIN DIAGNOSTIC EVENT DATA de.EvaluateAndPinAllDataIfNeeded(); //# GLOBAL FILTER //! Filtering on a global level must happen *after* logger name is set and properties pinned //! This is because filters are most likely use their values if (!MeetsFiltersCriteria(de, Name, config_ref.GlobalFilters)) return; //# GET SINKS // only use sinks which meet filtering criteria for this event var mustWaitForWriteSinks = (from s in config_ref.Sinks.MustWaitForWriteSinks where s.Filter.Evaluate(de, Name) select s).ToArray(); var fireAndForgetSinks = (from s in config_ref.Sinks.FireAndForgetSinks where s.Filter.Evaluate(de, Name) select s).ToArray(); var allSinks = mustWaitForWriteSinks.Concat(fireAndForgetSinks).ToArray(); //# REQUESTED CONTEXT DATA // get requested context data (requested by sinks via formatters) var isAdditionalContextDataRequested = false; var allRequestedContextData = new List<IDataRequest>(capacity: 27); for(int sink_ix = 0; sink_ix < allSinks.Length; sink_ix++) { var sink = allSinks[sink_ix]; var requestedContextData = sink.GetRequestedContextData(); for(int cd_ix = 0; cd_ix < requestedContextData.Count; cd_ix++) { var cd = requestedContextData[cd_ix]; allRequestedContextData.Add(cd); if (cd.Data == "Event.AdditionalContextData") isAdditionalContextDataRequested = true; } } allRequestedContextData = allRequestedContextData.Distinct().ToList(); // todo: check if performance of distinct is good enough //# INCLUDE DATA REQUESTED BY SINKS var requestedData = config_ref.ContextDataCollectors.Collect(allRequestedContextData); de.Properties.AddOrUpdateRange(requestedData); //# GET ADDITIONAL CONTEXT INFORMATION if (isAdditionalContextDataRequested) { var additionalContextdataCollectors = config_ref.AdditionalContextDataCollectors; for (int i = 0; i < additionalContextdataCollectors.Count; i++) { var collector = additionalContextdataCollectors[i]; // if collector has no filter then it should be used. if (collector.Filter == null) { de.AdditionalContextData.AddOrUpdateRange(collector.CollectData()); de.PinAdditionalContextDataIfNeeded(); break; } if (collector.Filter.Evaluate(de, this.Name)) { de.AdditionalContextData.AddOrUpdateRange(collector.CollectData()); de.PinAdditionalContextDataIfNeeded(); break; } } } //# WRITE TO SINKS if (mustWaitForWriteSinks.Length > 0) { if (mustWaitForWriteSinks.Length == 1) { var sink = mustWaitForWriteSinks.Single(); sink.Write(de); } else { ParallelExtensions.ForEach(mustWaitForWriteSinks, (sink) => { sink.Write(de); }); } } // then write to all fire-and-forget sinks if (fireAndForgetSinks.Length > 0) ParallelExtensions.ForEachNoWait( fireAndForgetSinks, (sink) => { if (sink.Filter.Evaluate(de, Name)) sink.Write(de); }); } catch (Exception ex) { InternalTrace.Error(ex, "Failed to log diagnostic event."); } }