Logger.ShouldLog behavior question

Topics: Logging Application Block
May 24, 2007 at 11:18 PM
Maybe I’m not understanding something, but Logger.ShouldLog doesn’t appear to work the way I would expect it to. My assumption was that it would tell me if a given LogEvent would be handled by any of the configured trace listeners.

I’ve got some diagnostic information that takes a fair amount of time to produce. I don’t want to generate it, unless it’s actually going to be logged. My attempt to handle this was to create a LogEvent, set the categories, set the priority (Verbose in this case) and call Logger.ShouldLog. It appears that Logger.ShouldLog is always returning true, even when all the log sources are configured with a switchLevel=”Warning”. The test I’m interested in exists in the private LogSource method ShouldTrace, but that doesn’t appear to get called from Logger.ShouldLog.

Based on the code it looks like the only thing that Logger.ShouldLog checks are filters. Is this correct and if so, is this intended? Is there any way perform the check I’m looking for without modifying EntLib source?

Thanks,
Michael O. Schoenman
May 25, 2007 at 1:59 AM
Hi Michael -

Your understanding is correct - Logger.ShouldLog only checks filters. If you are using switch levels on individual sources, this won't be taken into account. Unfortunately you'll need to modify the code to get what you want.

Tom
May 25, 2007 at 1:19 PM
Tom-

Thanks for the quick reply. I was afraid that was what you were going to say. I think I can see what I'm gonig to have to modify to get the bahavior I want and it's not horrible, but I hate that I'm going to have to re-integrate it everytime we update to a new EntLib release.

Is what I'm doing (trying to see if a LogEntry will actually be logged) that far from the mainstream? It seems like this would be something that would come up fairly often. Is there some other way to approach the problem that I'm not seeing?

Thanks again for your help,
-Michael O. Schoneman
Jul 19, 2007 at 11:58 PM
Hello Michael and Tom,

Just wanted to mention that I also would like to check severity in regards to the ShouldLog filtering. If either of you could save me the effort of re-inventing this functionality by providing code samples; that would be appreciated.

As I do not believe Michael and myself have unusual requests, if this could be incorporated into future releases, that would also be appreciated.

Kind regards,
Jeff
Sep 24, 2007 at 11:03 AM
Hi all,
I have the same question. I would expect to be able to tell if something will be written to the log, in order to avoid calling the logger if not, to improve performance. I had the following idea but I need some help since I'm new to EntLib:
  1. Can we somehow get the list of available sources and see from them if a Source Level is enabled?
  2. If not, can we somehow get the current configuration and from there to get the sources configuration and see if a specific Source Level is enabled? By searching a little I found out that to get the configuration (LoggingSettings.GetLoggingSettings) you need an IConfigurationSource instance, but I don't know where to find this.
Sep 24, 2007 at 12:37 PM
Hi,


papadi wrote:
Can we somehow get the list of available sources and see from them if a Source Level is enabled?

Unfortunately, no.


If not, can we somehow get the current configuration and from there to get the sources configuration and see if a specific Source Level is enabled? By searching a little I found out that to get the configuration (LoggingSettings.GetLoggingSettings) you need an IConfigurationSource instance, but I don't know where to find this.


You can do ConfigurationSourceFactory.Create() to get an instance equivalent to the one used by the Logging block.

Hope this helps,
Fernando
Sep 25, 2007 at 7:15 AM
It seems thought I cannot use configuration to tell if a specific log entry will be written in this case. The goal is to see if the log entry will finally be written to the log, depending on it's severity which is of System.Diagnostics.TraceEventType type. However the SourceLevels property in a Source is of System.Diagnostics.SourceLevels type.
Is there any relation between those two?
Sep 25, 2007 at 7:35 AM
I will partially answer myself. Yes there is a relation between SourceLevels and TraceEventType. And this is part of the .NET Framework and not part of EntLib. More info here http://msdn2.microsoft.com/en-us/library/system.diagnostics.sourcelevels(VS.80).aspx.

So, back to the main issue.

tomhollander wrote:
Unfortunately you'll need to modify the code to get what you want.

Instead of modifying the code to get this feature (which requires re-integrating this modification any time there is a new release of EntLib), do you guys think that it's a good idea to use the configuration to tell if a log entry of specific category and severity will be logged or not?
Sep 29, 2007 at 1:16 PM
Edited Sep 29, 2007 at 1:20 PM
Do not apply formatting
Here's a filter class that i think should do the trick:



public class CategorySourceFilter : Microsoft.Practices.EnterpriseLibrary.Logging.Filters.LogFilter
{

#region Fields
Dictionary<string, SourceLevels> categorySources;
SourceLevels allEventsSourceLevel = SourceLevels.Off;
SourceLevels unprocessedSourceLevel = SourceLevels.Off;
#endregion


#region Constructors

/// <summary>
/// Initializes a new instance of the <see cref="CategorySourceFilter"/> class.
/// </summary>
/// <param name="nvPairs">The nv pairs.</param>
public CategorySourceFilter(NameValueCollection nvPairs) : this("CategorySourceFilter") { }
/// <summary>
/// Constructor
/// </summary>
/// <param name="name">The name of the filter.</param>
public CategorySourceFilter(string name)
: base(name)
{

categorySources = new Dictionary<string, SourceLevels>();
IConfigurationSource configSource = ConfigurationSourceFactory.Create();

LoggingSettings settings = LoggingSettings.GetLoggingSettings(configSource);


foreach (TraceSourceData source in settings.TraceSources)
{
if (source.TraceListeners.Count > 0 && source.DefaultLevel != SourceLevels.Off)
{
if (!categorySources.ContainsKey(source.Name))
{
categorySources.Add(source.Name, source.DefaultLevel);
}
else
{
categorySourcessource.Name = categorySourcessource.Name | source.DefaultLevel;
}

}
}

if (settings.SpecialTraceSources.AllEventsTraceSource.TraceListeners.Count > 0 && settings.SpecialTraceSources.AllEventsTraceSource.DefaultLevel != SourceLevels.Off)
{

allEventsSourceLevel = settings.SpecialTraceSources.AllEventsTraceSource.DefaultLevel;
}

if (settings.SpecialTraceSources.NotProcessedTraceSource.TraceListeners.Count > 0 && settings.SpecialTraceSources.NotProcessedTraceSource.DefaultLevel != SourceLevels.Off)
{

unprocessedSourceLevel = settings.SpecialTraceSources.NotProcessedTraceSource.DefaultLevel;
}
}
#endregion

#region Methods
/// <summary>
/// Filters the log entry. Return false means the log should be ignored.
/// </summary>
/// <param name="log">The log entry to filter.</param>
/// <returns>True to Log, false to ignore.</returns>
public override bool Filter(LogEntry log)
{
//check the "all" special category
if (((int)log.Severity & (int)allEventsSourceLevel) != 0)
{
return true;
}

//check regular categories
foreach (string category in log.Categories)
{
if (categorySources.ContainsKey(category))
{
if (((int)log.Severity & (int)categorySourcescategory) != 0)
{
return true;
}
}
}

//check the "unprocessed" special category
if (((int)log.Severity & (int)unprocessedSourceLevel) != 0)
{
return true;
}

return false;
}
#endregion
}
Sep 29, 2007 at 1:21 PM
unfortunately, the wiki did some mangling of my code. things that look like links should be enclosed in squarre brackets
Oct 27, 2010 at 2:59 PM

Anybody know if the above custom filter works, and if it is the best solution to this problem?

Oct 28, 2010 at 12:17 AM
Edited Oct 28, 2010 at 3:09 AM

I believe it would work although it doesn't check the Filter property of each trace listener associated to a category which is what was the initial requirement mentioned in this thread.  Thus, in the scenario where you pass a LogEntry which has a Severity equal to Information and your logging Category's Severity is also set to Information but its trace listener(s) Filter property is set to Error, that custom filter will still return true but actually, that log entry won't get logged.  If you need to also check each trace listeners, here's a sample code which I have tested that it works.

[ConfigurationElementType(typeof(CustomLogFilterData))] //by the way, this is also missing in the code posted above.   This is required.
public class CategorySourceFilter : Microsoft.Practices.EnterpriseLibrary.Logging.Filters.LogFilter
    {

        #region Fields
        Dictionary<TraceSourceData, TraceListenerDataCollection> categorySources = new Dictionary<TraceSourceData,TraceListenerDataCollection>();
        #endregion


        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="CategorySourceFilter"/> class.
        /// </summary>
        /// <param name="nvPairs">The nv pairs.</param>
        public CategorySourceFilter(NameValueCollection nvPairs) : this("CategorySourceFilter") { }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">The name of the filter.</param>
        public CategorySourceFilter(string name)
            : base(name)
        {

            IConfigurationSource configSource = ConfigurationSourceFactory.Create();

            LoggingSettings settings = LoggingSettings.GetLoggingSettings(configSource);

            //creates a dictionary of logging categories and referenced trace listeners
            foreach (var category in settings.TraceSources)
            {
                if (category.TraceListeners.Count > 0)
                {
                    TraceListenerDataCollection tracelisteners = new TraceListenerDataCollection();

                    foreach (var traceListener in category.TraceListeners)
                    {
                        tracelisteners.Add(settings.TraceListeners.First(t => t.Name == traceListener.Name));
                    }

                    categorySources.Add(category, tracelisteners);
                }
            }

        }
        #endregion

        #region Methods
        /// <summary>
        /// Filters the log entry. Return false means the log should be ignored.
        /// </summary>
        /// <param name="log">The log entry to filter.</param>
        /// <returns>True to Log, false to ignore.</returns>
        public override bool Filter(LogEntry log)
        {
            foreach (string category in log.Categories)
            {
                KeyValuePair<TraceSourceData, TraceListenerDataCollection> categorySource 
                    = categorySources.FirstOrDefault(c => c.Key.Name == category);
                if (categorySource.Key != null)
                {
                    if (((int)log.Severity & (int)categorySource.Key.DefaultLevel) != 0)
                    {
                        //checks the Filter for each associated trace listeners of the category
                        foreach (var traceListener in categorySource.Value)
                        {
                            if ((int)log.Severity <= (int)traceListener.Filter)
                            {
                                return true;
                            }
                        }
                    }
                }
            }

            return false;
        }
        #endregion
}

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

Oct 28, 2010 at 1:23 PM

Thanks Sarah,

However, seeing the line 

if ((int)log.Severity <= (int)traceListener.Filter)

makes me confused again as to how SourceLevels and TraceEventTypes hang together at the traceListener level.

(See http://entlib.codeplex.com/Thread/View.aspx?ThreadId=228700).   

if (((int)log.Severity & (int)categorySource.Key.DefaultLevel) != 0)

makes sense to me.

Oct 29, 2010 at 12:12 AM

That was a typo, sorry.  It should be (((int)log.Severity & (int)traceListener.Filter) != 0)

 

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

Nov 15, 2010 at 6:33 PM
Edited Nov 17, 2010 at 2:12 PM

Hi

Was going through this code (specifically the Filter method override) and noticed the following; that is if I understand things correctly:

  • If I have two tracelisteners under one category , one has its severity = OFF and the other's severity = ALL, both tracelisteners will be executed as the code appears not to distinguish between the individual severity settings of each tracelistener/traceSource under a category but passes or fails them corporately...... Am i missing something?

I was hoping I could have two tracelisteners under one category and set their severity appropriately to filter / direct logging to different trace listeners using the CategorySourceFilter mentioned above.

 

 

Nov 16, 2010 at 11:46 PM

You're right asbailey, checking trace listeners under a category with at least one of them having a severity that meets the criteria will cause it to return true even if other trace listeners doesn't.  It is then a bit unnecessary and will only be useful for scenarios where all of the trace listeners under that category doesn't meet the minimum severity.  Thanks for the input.

I'm not sure though that I understand what is the behavior that you want, could you explain it a bit more?

 

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

Nov 17, 2010 at 2:58 PM

thanks for the reply Sarah

Basically, I was hoping to have just one category with multiple trace-listerners registered to it.

I wanted to then use the severity settings of each tracelistener to log or not log a message. I thought a custom filter would do the trick, however from my research so far I cannot find a way to implement this. From my basic testing, the EL logging framework honored the severity setting on the category, however ignored the severity settings on each individual tracelistener associated with that category.

Hope that clarifies things 

  

Nov 17, 2010 at 11:51 PM

Hi asbailey, the Severity Filter property of trace listeners are still being taken into account, but not during the call to Logger.ShouldLog method.  It is being checked when processing each trace listener.  The Minimum Severity filter property of the category though serves as the first line of check.  If your category's minimum severity is more restrictive than its referenced trace listeners, no logging will occur then. 

 

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

Nov 18, 2010 at 1:32 PM

Hey Sarah

Are you saying that EL will take the trace-listener severity settings into consideration automatically when deciding to log a message once they have passed the Category severity check?

From my testing, using just one category with a severity setting of ALL and two tracelisteners, both appeared to execute irrespective of their severity settings. It looked like EL just ignored their severity levels.

Am i missing something?

Thanks

Alistair

 

Nov 18, 2010 at 7:58 PM
Edited Nov 18, 2010 at 7:58 PM

Alistair

Have you seen the thread I link to above?

http://entlib.codeplex.com/Thread/View.aspx?ThreadId=228700

I think the severity filtering at tracelistener does work, but not as I expected it to.  

I find it almost useless, as one of the most obvious requirements is to route errors to a different destination to information.  

I read a review, written back in 2006 (can't find it now), about EL logging that said that it most missed the ability to apply custom filters at the tracelistener level.  That would resolve a lot for me.

Sean

Nov 18, 2010 at 11:05 PM

Alistair,

Does the thread posted by Sean describes and explains your scenario?

 

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

Nov 18, 2010 at 11:24 PM
Thanks for the input Sean. To answer your question Sarah, yes that is pretty much it, EL does not appear to honor the Severity settings on assigned tracelisteners! Cheers
Nov 18, 2010 at 11:34 PM

I see.  Just to make it clear and avoid confusion for others who may see this thread, EntLib trace listeners do filter based on Severity assigned to it.  It's just that it doesn't only allow what you set as the value of the Severity Filter, it also allows everything else that is more important than that severity.

 

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

Nov 23, 2010 at 9:11 PM

Hi Sarah

Just so I understand; you are saying that the Tracelisteners severity is influenced by other factors or sub-serviant to other factors. The only example of this, I can think of, is the Tracelistensers parent Category's severity setting?

Are there other settings / configurations that would stop a tracelistenser from being executed even though its severity is relevant to the LogEntry recieved?

 

Nov 23, 2010 at 10:37 PM

No, that's not what I'm saying.  I just clarified your statement that "EL does not appear to honor the Severity settings on assigned trace listeners!".   This is incorrect because EL does, but not as what you expected because your expectation is if you set the Severity filter of a trace listener to Information, you expect that it will only allow LogEntry with its Severity property set to TraceEventType.Information but what really happens is that it will also allow other log entries that are "more important" than informational logs example of which are error and critical logs (Error, Critical). 

On your last question, there is although I think you already know it and that is the LogEnabledFilter which when set to False prevents all logging to execute.

 

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