/// <summary> /// Writes an event to registered log instances /// </summary> /// <param name="type"> /// The type of event /// </param> /// <param name="props"> /// The event context properties /// </param> /// <param name="except"> /// The event exception /// </param> /// <param name="format"> /// The event message format string /// </param> /// <param name="objects"> /// The event message format parameters /// </param> private void DispatchEvent( EventType type, Object props, Exception except, String format, Object[] objects) { // sample timestamps at method start, so that // they are as close to the event time as possible Int64 timestamp = GetTimestamp(); DateTime time = DateTime.UtcNow; // ensure initialization/configuration is complete Initialize(); if (instances.Count > 0) { Boolean hasAsync = false; // create the event context, and attach // all local event properties EventContext ctx = new EventContext() { Timestamp = timestamp, Time = time, Type = type, Source = this.sourceType, Exception = except, MessageFormat = format, MessageParams = objects }; ctx.SetSourceProps(this.sourceType, this.sourceProps); ctx.SetEventProps(props); using (ConfigReadLock()) { // sample global state on the current execution // context/thread, so that the remainder of event // processing can occur concurrently if (activeContexts.Any()) ctx.SetGlobalProps(activeContexts.ToDictionary( m => m.Key, m => { try { return m.Value(); } catch { return null; } } )); // dispatch to any synchronous log instances foreach (Instance instance in instances) if (instance.Synchronous) DispatchEventInstance(instance, ctx); else hasAsync = true; } // dispatch to asynchronous log instances // . only start a new dispatcher work item if we sample a count // of 0 in the event queue // . there is a potential race here in which multiple threads could // sample a counter of 0, which would simply result in multiple // dispatcher work items // . in addition, it is possible for a dispatcher to completely drain // the queue and exit after the logging thread samples a nonzero count, // although this would require the low-priority dispatcher to dispatch // the last event faster than the logger could enqueue the next event, // the only result being an event remaining in the queue longer // . these compromises are preferable to more heavy-handed synchronization // mechanisms that would cause more contention on the logger and // reduce application throughput if (hasAsync) { Boolean startDispatch = (eventQueue.Count == 0); eventQueue.Enqueue(ctx); if (startDispatch) ThreadPool.UnsafeQueueUserWorkItem(AsyncEventDispatcher, null); } } }
/// <summary> /// Dispatches a log event to a single log instance /// </summary> /// <param name="instance"> /// The log instance to receive the event /// </param> /// <param name="ctx"> /// The event to dispatch /// </param> private static void DispatchEventInstance(Instance instance, EventContext ctx) { if (instance.Filter.Evaluate(ctx)) try { instance.Logger.Log(new[] { new Event(ctx, instance.Properties) }); } catch { } }
/// <summary> /// Matches an event context property to a property /// </summary> /// <param name="context"> /// The event context containing the property to compare /// </param> /// <param name="prop"> /// The property to match /// </param> /// <returns> /// True if the event context matches the property /// False otherwise /// </returns> private Boolean Match(EventContext context, Property prop) { Object eValue = context.GetProperty(prop.Name); Object pValue = prop.Value; if (eValue == null && pValue == null) return true; if (eValue == null || pValue == null) return false; try { return (prop.Except != prop.Comparer.Equals(eValue, pValue)); } catch { return false; } }
/// <summary> /// Evaluates a filter against an event context /// </summary> /// <param name="context"> /// The event context to query /// </param> /// <returns> /// True if the event context matches the filter /// False otherwise /// </returns> internal Boolean Evaluate(EventContext context) { // evaluate property value inclusion/exclusion if (this.ExcludeProps.Any(p => Match(context, p))) return false; if (this.IncludeProps.Any()) if (!this.IncludeProps.Any(p => Match(context, p))) return false; return true; }
/// <summary> /// Initializes a new event instance /// </summary> /// <param name="context"> /// The current event context /// </param> /// <param name="propNames"> /// The list of properties to sample /// from the event context /// </param> internal Event(EventContext context, IEnumerable<String> propNames) { this.props = propNames .Select(p => new KeyValuePair<String, Object>(p, context.GetProperty(p))) .ToList() .AsReadOnly(); this.map = new Hashtable( this.props.ToDictionary(p => p.Key, p => p.Value), StringComparer.OrdinalIgnoreCase ); }