LogFilter for matching TraceListeners?

Topics: Logging Application Block
Aug 4, 2010 at 8:55 PM
I've just learned the fact that Logger.ShouldLog(...) only examines filters, not whether or not there are TraceListeners that would actually accept the LogEntry once it's past the filters. This means that if I modify my TraceListener configuration to not include expensive LogEntries (by expensive, I mean it is intensive/slow to generate the message) it won't affect the return value from Logger.ShouldLog(). What I'd like is to have Logger.ShouldLog() also consider whether or not any TraceListeners would actually listen to the LogEntry and, if not, return false. Has anyone built such a LogFilter? If not, is it even possible? I've only scratched the surface of the Logging API and haven't yet determined this, but I wanted to ask the community before I dove any deeper.
Aug 5, 2010 at 6:25 AM

I believe your observation is correct Trinition.

Is my understanding correct that what you would want is to make sure that there are TraceListeners for the Categories defined/added in your LogEntry then continue with writing the log if there are tracelisteners exist, right?

If this is the case, then my answer is yes. You can do this in your own Custom Log Filter checking the value of the Logger class for the TraceSources and Listeners available prior doing the actual log. In your custom filter, in the Filter method you can try having something like this to check the number of tracelisteners available:

            foreach (string s in log.Categories)
            {
                if (Logger.Writer.TraceSources.ContainsKey(s))
                {
                    if (Logger.Writer.TraceSources[s].Listeners.Count > 0)
                    {
                        
                    }
                }
            }

Another way I can think of is by extending the LogWriter class to have something like the ShouldLog method which also checks available listeners. See sample below;

/// <summary>
    /// LogWriter Extension Class
    /// </summary>
    public static class LogWriterExtension
    {
        /// <summary>
        /// Extended method for ShouldLog method
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="log"></param>
        /// <returns></returns>
        public static bool ShouldLogExtended(this LogWriter writer, LogEntry log)
        {
            if (writer.ShouldLog(log))
            {
                foreach (string s in log.Categories)
                {
                    if (writer.TraceSources.ContainsKey(s))
                    {
                        if (writer.TraceSources[s].Listeners.Count > 0)
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    }

Therefore instead of using this

            if (writer.ShouldLog(log))
            {
                writer.Write(log);
            }

You can utilize the extended method

            if (writer.ShouldLogExtended(log))
            {
                writer.Write(log);
            }

Hope this helps.

Gino Terrado
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

 

Aug 6, 2010 at 6:55 PM

Gino, thank you for the suggestion.  "ShouldLogExtended" is exactly what I wanted!

I took me a while to reply because I was trying it out.  I think it's pretty close.  Unfortunately, my log entry's categories (e.g. "Instrumentation", "Vendor") to not appear as keys in the TraceSources keys.  The keys in my case include the pseudo-categories like "General" which will still match when it's actually logged but obviously aren't an exact string match.

However, the fix seems obvious: code in some conditions for the special categories.  In particular, "General" can be checked independent of the LogEntry's categories because if "General" is on, every category will get logged (if my understanding is correct). 

Aug 9, 2010 at 2:39 AM
Edited Aug 9, 2010 at 10:38 AM

The purpose of the code which Gino had posted is just to check if any of the trace listeners are actually going to process the log entry. In your scenario, if General is added to your logEntry's Categories property, and the General category has trace listener(s), then the trace listener(s) under the General category will write that logEntry but that doesn't mean that every other category's trace listeners will process that logEntry. 

 

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

Aug 9, 2010 at 4:00 PM

That's not quite what I found in practice because the "General" category bevahed like a pseudo-category.  I won't pretend to fully understand what I'm talking about, but when I had no trace listeners specifically set to listen to my category ("VendorXml"), I still had trace listeners that would log it even though the code check above indicated it wouldn't be logged.  Here's my categorySources and specialSources:

    <categorySources>
      <add switchValue="All" name="General">
        <listeners>
          <add name="Default Trace Listener" />
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events">
        <listeners>
          <add name="Default Trace Listener" />
        </listeners>
      </allEvents>
      <notProcessed switchValue="All" name="Unprocessed Category" />
      <errors switchValue="All" name="Logging Errors &amp; Warnings">
        <listeners>
          <add name="Default Trace Listener" />
        </listeners>
      </errors>
    </specialSources>

Aug 10, 2010 at 5:13 AM

Hi Trinition,

It will still indeed log an en entry given that your allEvents specialSources is configured with a Trace Listeners wherein take note that this category receives all log entries and get it logged. Unfortunatey the ShouldLogExtended method above doesn't check for specialSources category. I know I have missed to consider this part earlier and should have done the implementation for all sources. So to address this, you can actually utilize the LogWriter's GetMatchingTraceSources method. ShouldLogExtended may now look something like this:

        public static bool ShouldLogExtended(this LogWriter writer, LogEntry log)
        {
            if (writer.ShouldLog(log))
            {
                var traceSources = writer.GetMatchingTraceSources(log).GetEnumerator();
                while (traceSources.MoveNext())
                {
                    if (traceSources.Current.Listeners.Count == 0)
                    {
                        return false;
                    }
                }
                return true;

            }
            return false;
        }

In this, it won't allow to proceed with the logging of the LogEntry whenever any Category does not contain any Listener configured with it. Well of course, what will be the content of the method would still base from your own requirements/needs. Hope this address your concerns.

Gino Terrado
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com