LoggingApplicationBlock and EventViewer category

Topics: Logging Application Block
Mar 27, 2009 at 12:03 PM
Hello. I'm new on this site.

I've been using Enterprise Library LoggingApplicationBlock to log messages to files and event viewer. I even integrated my logging logic with Unity. Unfortunately, when I'm sending something to my Event Viewer, the event doesn't appear assigned to any category; that is, let's say, I have an event of type Information, at a specific date and time, having a source (which I registered with a custom installer and using installutil), a NONE category, an Event ID, N/A User, and a computer.
My question is, how should I configure the trace event listener so that the category sources to appear in eventviewer in the category column? Or should I write my own trace event listener? I've tried the things suggested in here
http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.languages.vb/2008-02/msg00090.html<o:p></o:p> but it didn't work, I haven't got any category in my event sources.
Mar 27, 2009 at 1:04 PM

Hi,

You don’t have to write your own trace listener. If all were set correctly then there will be no problem writing logs on event viewer (the default).  Are you pertaining to the “None” value assigned on the Task Category column? This is what I’ve seen on my Vista machine and I don’t know if it’s the same case with other OS.  But if you’re going to look at the General tab you will see that values were set correctly based on the configuration file. J
Mar 27, 2009 at 1:14 PM
My computer at work has a Win XP SP3. My problem is that, altough the event is written in the log, it has no information on the Category column. By inspecting the event (double clicking it) I see everything is ok with it. I found an earlier post of David Hayden ( http://entlib.codeplex.com/Thread/View.aspx?ThreadId=10540 ) in which he says I have to register the categories somewhere in the registries. The problem is that, although I have already followed those steps (see my initial post and the link I provided there), I can't map entries to categories, nor does it seem to have registered any categories on my log sources (by right clicking the log name you see log sources and then categories).
Although the logging made using ELLAB is very useful, it would take a second less to identify the category of the entry, without opening the entry itself. Anyway, now I am puzzled by this and I'll post the solution once that I find it.
If someone has already experienced that, his/her hints would be valuable.

Thanks alot.
Mar 27, 2009 at 2:48 PM
Edited Mar 27, 2009 at 2:50 PM
I have succeded in creating categories for a log. The way I managed to do that is a little different from this link http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.languages.vb/2008-02/msg00090.html :
I created in MS-DOS the file with categories in this format:
MessageId=0x1
Language=English
General
.

Please notice the hexadecimal MessageId and the empty line after the last category. I used MS DOS editor because the windows editor kind of added some other non printable characters. I saved this file as Categories.mc. Then I used VS 2008 Command Prompt, I executed mc Categories.mc, then rc Categories.rc, then link /dll /noentry /machine:X86 /out:Categories.dll Categories.res.

These are exactly like in the article I linked. The difference begins now. After creating the needed log sources I had to relly on regedit to create registry keys because I just couldn't link the categories dll to the sources in an eventlog installer.
(
System.Diagnostics.EventLogInstaller categoriesCEInstaller = new System.Diagnostics.EventLogInstaller();
            categoriesCEInstaller.Log = "XXX";
            categoriesCEInstaller.Source = "YYYYYY";
            categoriesCEInstaller.CategoryCount = 6;
            categoriesCEInstaller.CategoryResourceFile = @"c:\Categories.dll";
didn't do the trick.
)

For each log source I had to add to keys, one DWORD CategoryCount, in which I stored the number of Categories, and one named CategoryMessageFile, of type Expandable String Value, in which I stored the path (including the name) to the dll I created.

After this, I was able to see the event sources and the associated categories.

I am still having problems at adding a category to an event entry.
Mar 30, 2009 at 7:23 AM
Edited Mar 30, 2009 at 7:28 AM

Putting  a value on the category column is beyond the scope of the Logging Application Block. Please refer to this link for more information : http://entlib.codeplex.com/Thread/View.aspx?ThreadId=10540.

Mar 30, 2009 at 7:45 AM
Thanks for the link. I'm stuck for moment. As soon as I'll find a way to do it I'll post here.
Mar 31, 2009 at 6:56 AM
For starters, I'd suggest reading http://msdn.microsoft.com/en-us/library/system.diagnostics.eventloginstaller.categorycount.aspx
Mar 31, 2009 at 7:11 AM
Thanks for the info.
Apr 1, 2009 at 3:14 PM
I am almost done with this thing. I only have a couple of small issues.
To implement category logging, I had to write a custom EventLogTraceListener:CustomTraceListener, a custom FormattedEventLogTraceListener, a custom FormattedTraceListenerBase:CustomTraceListener, IInstrumentationEventProvider and a custom FormattedTraceListenerWrapperBase: custom FormattedTraceListenerBase. Just like the way it's made in entlib.
Now I have a fully formatted event log listener. The problem is that, although I set in web.config the attributes for the custom formatted event log listener and that I'm able to see them in the code, I don't know how to pass them to the custom eventlog trace listener, to give to the EventLog class the correct log and source values. How is the attributes passing method implemented in entlib? I couldn't find it with Lutz's Reflector (or I didn't look out well).
Apr 2, 2009 at 8:29 AM
Edited Apr 2, 2009 at 8:32 AM
I completed the EventLogger which logs categories. :D. The listing follows.

The formatted full fledged event logger which should be used. Three attributes are needed: Log, Source and MachineName, just like in the case of the original formatted event logger (and which already exist). It looks almost like the entlib version.
LogSource is an enum with keys that are mapped with the event sources and with descriptions that are mapped with the categories defined in the dll file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

namespace ****
{
    [ConfigurationElementType(typeof(CustomTraceListenerData))]
    public class EnhancedFormattedEventLogTraceListener : EnhancedFormattedTraceListenerWrapperBase
    {
        // Fields
        public const string DefaultLogName = "";
        public const string DefaultMachineName = ".";

        // Methods
        public EnhancedFormattedEventLogTraceListener()
            : base(new EnhancedEventLogTraceListener())
        {
        }

        public EnhancedFormattedEventLogTraceListener(ILogFormatter formater)
            : base(new EnhancedEventLogTraceListener(), formater)
        {
        }

        public EnhancedFormattedEventLogTraceListener(EventLog eventLog)
            : base(new EnhancedEventLogTraceListener(eventLog))
        {
        }

        public EnhancedFormattedEventLogTraceListener(string source)
            : base(new EnhancedEventLogTraceListener(source))
        {
        }

        public EnhancedFormattedEventLogTraceListener(EventLog eventLog, ILogFormatter formatter)
            : base(new EnhancedEventLogTraceListener(eventLog), formatter)
        {
        }

        public EnhancedFormattedEventLogTraceListener(string source, ILogFormatter formatter)
            : base(new EnhancedEventLogTraceListener(source), formatter)
        {
        }

        public EnhancedFormattedEventLogTraceListener(string source, string log, ILogFormatter formatter)
            : base(new EnhancedEventLogTraceListener(new EventLog(log, ".", source)), formatter)
        {
        }

        public EnhancedFormattedEventLogTraceListener(string source, string log, string machineName, ILogFormatter formatter)
            : base(new EnhancedEventLogTraceListener(new EventLog(log, NormalizeMachineName(machineName), source)), formatter)
        {
        }

        private static string NormalizeMachineName(string machineName)
        {
            if (!string.IsNullOrEmpty(machineName))
            {
                return machineName;
            }
            return ".";
        }

    }
}

The base wrapper class:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;

namespace *****
{
    public class EnhancedFormattedTraceListenerWrapperBase : EnhancedFormattedTraceListenerBase
    {
        // Fields
        private EnhancedEventLogTraceListener slaveListener;
        public string Log
        {
            get
            {
                return base.Attributes["Log"];
            }
        }

        public string Source
        {
            get
            {
                return base.Attributes["Source"];
            }
        }

        public string MachineName
        {
            get
            {
                return base.Attributes["MachineName"];
            }
        }

        // Methods
        public EnhancedFormattedTraceListenerWrapperBase()
        {
        }

        public EnhancedFormattedTraceListenerWrapperBase(EnhancedEventLogTraceListener slaveListener)
        {
            this.slaveListener = slaveListener;
        }

        public EnhancedFormattedTraceListenerWrapperBase(EnhancedEventLogTraceListener slaveListener, ILogFormatter formater)
            : base(formater)
        {
            this.slaveListener = slaveListener;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.slaveListener.Dispose();
            }
        }

        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType severity, int id, params object[] data)
        {
            if (!this.slaveListener.IsEventLogSet)
                this.slaveListener = new EnhancedEventLogTraceListener(Log, MachineName, Source);
            this.slaveListener.TraceData(eventCache, source, severity, id, data);
        }

        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType severity, int id, object data)
        {
            if (!this.slaveListener.IsEventLogSet)
                this.slaveListener = new EnhancedEventLogTraceListener(Log, MachineName, Source);
            if (data is LogEntry)
            {
                if (base.Formatter != null)
                {
                    this.slaveListener.TraceData(eventCache, source, severity, id, base.Formatter.Format(data as LogEntry));
                }
                else
                {
                    this.slaveListener.TraceData(eventCache, source, severity, id, data);
                }
                base.InstrumentationProvider.FireTraceListenerEntryWrittenEvent();
            }
            else
            {
                this.slaveListener.TraceData(eventCache, source, severity, id, data);
            }
        }

        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType severity, int id, string message)
        {
            if (!this.slaveListener.IsEventLogSet)
                this.slaveListener = new EnhancedEventLogTraceListener(Log, MachineName, Source);
            this.slaveListener.TraceEvent(eventCache, source, severity, id, message);
        }

        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType severity, int id, string format, params object[] args)
        {
            if (!this.slaveListener.IsEventLogSet)
                this.slaveListener = new EnhancedEventLogTraceListener(Log, MachineName, Source);
            this.slaveListener.TraceEvent(eventCache, source, severity, id, format, args);
        }

        public override void Write(string message)
        {
            this.slaveListener.Write(message);
        }

        public override void WriteLine(string message)
        {
            this.slaveListener.WriteLine(message);
        }

        // Properties
        public TraceListener SlaveListener
        {
            get
            {
                return this.slaveListener;
            }
        }

    }
}

The base class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.Logging.Instrumentation;
using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
using Microsoft.Practices.EnterpriseLibrary.Common.Instrumentation;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;

namespace ****
{
    public abstract class EnhancedFormattedTraceListenerBase : CustomTraceListener, IInstrumentationEventProvider
    {
        // Fields
        private LoggingInstrumentationProvider instrumentationProvider;

        // Methods
        public EnhancedFormattedTraceListenerBase()
        {
            this.instrumentationProvider = new LoggingInstrumentationProvider();
        }

        public EnhancedFormattedTraceListenerBase(ILogFormatter formatter)
        {
            this.Formatter = formatter;
            this.instrumentationProvider = new LoggingInstrumentationProvider();
        }

        public object GetInstrumentationEventProvider()
        {
            return this.instrumentationProvider;
        }

        protected LoggingInstrumentationProvider InstrumentationProvider
        {
            get
            {
                return this.instrumentationProvider;
            }
        }

        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
            if ((base.Filter == null) || base.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
            {
                string message = string.Empty;
                if (data != null)
                {
                    message = data.ToString();
                    this.WriteLine(message);
                }
            }
        }

        public override bool IsThreadSafe
        {
            get
            {
                return true;
            }
        }
    }
}

... and the enhanced event log trace listener

[ConfigurationElementType(typeof(CustomTraceListenerData))]
    public class EnhancedEventLogTraceListener : CustomTraceListener
    {
        // Fields
        private EventLog eventLog;
        private bool nameSet;

        public string Log { get; set; }
        public string Source { get; set; }
        public string MachineName { get; set; }
        public bool IsEventLogSet
        {
            get
            {
                if (eventLog == null)
                    return false;
                if( String.IsNullOrEmpty(Log) || String.IsNullOrEmpty(MachineName) || String.IsNullOrEmpty(Source) )
                    return false;
                return true;
            }
        }

        // Methods
        public EnhancedEventLogTraceListener()
        {
        }

        public EnhancedEventLogTraceListener(EventLog eventLog)
        {
            this.eventLog = eventLog;
        }

        public EnhancedEventLogTraceListener(string source)
        {
            this.eventLog = new EventLog();
            this.eventLog.Source = source;
        }

        public EnhancedEventLogTraceListener(string log, string machineName, string source)
        {
            if (log == null)
                log = ".";
            if (machineName == null)
                machineName = ".";
            if (source == null)
                source = ".";
            if (eventLog == null)
                eventLog = new EventLog(log, machineName, source);
            else
            {
                eventLog.Log = log;
                eventLog.Source = source;
            }
        }

        public override void Close()
        {
            if (this.eventLog != null)
            {
                this.eventLog.Close();
            }
        }

        private EventInstance CreateEventInstance(TraceEventType severity, long messageId, int categoryId)
        {
            if (messageId > 0xffff)
            {
                messageId = 0xffff;
            }
            if (messageId < 0)
            {
                messageId = 0;
            }
            EventInstance instance = new EventInstance(messageId, categoryId);
            if ((severity == TraceEventType.Error) || (severity == TraceEventType.Critical))
            {
                instance.EntryType = EventLogEntryType.Error;
                return instance;
            }
            if (severity == TraceEventType.Warning)
            {
                instance.EntryType = EventLogEntryType.Warning;
                return instance;
            }
            instance.EntryType = EventLogEntryType.Information;
            return instance;
        }

        private EventInstance CreateEventInstance(TraceEventType severity, int id)
        {
            if (id > 0xffff)
            {
                id = 0xffff;
            }
            if (id < 0)
            {
                id = 0;
            }
            EventInstance instance = new EventInstance((long)id, 0);
            if ((severity == TraceEventType.Error) || (severity == TraceEventType.Critical))
            {
                instance.EntryType = EventLogEntryType.Error;
                return instance;
            }
            if (severity == TraceEventType.Warning)
            {
                instance.EntryType = EventLogEntryType.Warning;
                return instance;
            }
            instance.EntryType = EventLogEntryType.Information;
            return instance;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.Close();
            }
        }

        private List<string> GetCategories(object data)
        {
            return data.ToString().Split("\n".ToCharArray()).First(item => item.Contains("Category:")).Split(":".ToCharArray())[1].Split(",".ToCharArray()).Select(item => item.Trim()).ToList<string>();
        }

        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType severity, int id, params object[] data)
        {
            if ((base.Filter == null) || base.Filter.ShouldTrace(eventCache, source, severity, id, null, null, null, data))
            {
                EventInstance instance = this.CreateEventInstance(severity, id);
                StringBuilder builder = new StringBuilder();
                if (data != null)
                {
                    for (int i = 0; i < data.Length; i++)
                    {
                        if (i != 0)
                        {
                            builder.Append(", ");
                        }
                        if (data[i] != null)
                        {
                            builder.Append(data[i].ToString());
                        }
                    }
                }
                this.eventLog.WriteEvent(instance, new object[] { builder.ToString() });
            }
        }

        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType severity, int id, object data)
        {
            if ((base.Filter == null) || base.Filter.ShouldTrace(eventCache, source, severity, id, null, null, null, new object[] { data }))
            {
                foreach(string category in GetCategories(data))
                {
                    Int16 categoryId = Int16.Parse(EnumHelper.EnumParseKey<LogCategory>(category, true).GetDescription());
                    EventInstance instance = this.CreateEventInstance(severity, id,categoryId);
                    this.eventLog.WriteEvent(instance, new object[] { data });
                }
            }
        }

        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType severity, int id, string message)
        {
            if ((base.Filter == null) || base.Filter.ShouldTrace(eventCache, source, severity, id, message, null, null, null))
            {
                EventInstance instance = this.CreateEventInstance(severity, id);
                this.eventLog.WriteEvent(instance, new object[] { message });
            }
        }

        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType severity, int id, string format, params object[] args)
        {
            if ((base.Filter == null) || base.Filter.ShouldTrace(eventCache, source, severity, id, format, args, null, null))
            {
                EventInstance instance = this.CreateEventInstance(severity, id);
                if (args == null)
                {
                    this.eventLog.WriteEvent(instance, new object[] { format });
                }
                else if (string.IsNullOrEmpty(format))
                {
                    string[] values = new string[args.Length];
                    StringBuilder builder = new StringBuilder();
                    if (args != null)
                    {
                        for (int i = 0; i < args.Length; i++)
                        {
                            if (i != 0)
                            {
                                builder.Append(", ");
                            }
                            if (args[i] != null)
                            {
                                builder.Append(args[i].ToString());
                            }
                        }
                    }
                    this.eventLog.WriteEvent(instance, values);
                }
                else
                {
                    this.eventLog.WriteEvent(instance, new object[] { string.Format(CultureInfo.InvariantCulture, format, args) });
                }
            }
        }

        public override void Write(string message)
        {
            if (this.eventLog != null)
            {
                this.eventLog.WriteEntry(message);
            }
        }

        public override void WriteLine(string message)
        {
            this.Write(message);
        }

        // Properties
        public EventLog EventLog
        {
            get
            {
                return this.eventLog;
            }
            set
            {
                this.eventLog = value;
            }
        }

        public override string Name
        {
            get
            {
                if (!this.nameSet && (this.eventLog != null))
                {
                    this.nameSet = true;
                    base.Name = this.eventLog.Source;
                }
                return base.Name;
            }
            set
            {
                this.nameSet = true;
                base.Name = value;
            }
        }
    }
}


Apr 2, 2009 at 11:22 AM
Now I'll try dealing with some custom TraceListenerData so that IsEventLogSet would dissappear.

Aug 19, 2009 at 4:58 AM
Edited Aug 19, 2009 at 4:58 AM

You solved the exact issue I was trying to figure out, thanks for posting the code. Is there any way you can post the source as a set of files?

Aug 25, 2009 at 4:46 AM

Would it possible for you to also post the implementation of EnumHelper class and the LogCategory enumeration? Greta work!

Aug 25, 2009 at 5:23 AM

The cattegories you use in LogCategory enumeration should appear in the logging application block in web config. The basic implementation of EnumParseKey and etc is

public static T EnumParse<T>(this string value, bool ignoreCase)
        {

            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            value = value.Trim();

            if (value.Length == 0)
            {
                throw new ArgumentException("Must specify valid information for parsing in the string.", "value");
            }

            Type t = typeof(T);

            if (!t.IsEnum)
            {
                throw new ArgumentException("Type provided must be an Enum.", "T");
            }

            T enumType = (T)Enum.Parse(t, value, ignoreCase);

            return enumType;
        }


        public static T EnumParseKey<T>(this string value, bool ignoreCase)
        {

            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            value = value.Trim();

            if (value.Length == 0)
            {
                throw new ArgumentException("Must specify valid information for parsing in the string.", "value");
            }

            Type type = typeof(T);

            if (!type.IsEnum)
            {
                throw new ArgumentException("Type provided must be an Enum.", "T");
            }

            Array enumValues = Enum.GetValues(type);

            foreach (Enum enumVal in enumValues)
            {
                if (GetKey(enumVal).CompareTo(value) == 0)
                    return (T)Enum.Parse(type, enumVal.ToString(), ignoreCase);
            }
            throw new ArgumentException("Must specify a valid Enum key value.", "value");
        }

        public static T EnumParseKey<T>(this string value, bool ignoreCase, bool ignoreSpaces)
        {

            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            if (!ignoreSpaces)
            {
                value = value.Trim();

                if (value.Length == 0)
                {
                    throw new ArgumentException("Must specify valid information for parsing in the string.", "value");
                }
            }

            Type type = typeof(T);

            if (!type.IsEnum)
            {
                throw new ArgumentException("Type provided must be an Enum.", "T");
            }

            Array enumValues = Enum.GetValues(type);

            foreach (Enum enumVal in enumValues)
            {
                if (GetKey(enumVal).CompareTo(value) == 0)
                    return (T)Enum.Parse(type, enumVal.ToString(), ignoreCase);
            }
            throw new ArgumentException("Must specify a valid Enum key value.", "value");
        }

        public static T EnumParseDescription<T>(this string value, bool ignoreCase)
        {

            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            value = value.Trim();

            if (value.Length == 0)
            {
                throw new ArgumentException("Must specify valid information for parsing in the string.", "value");
            }

            Type type = typeof(T);

            if (!type.IsEnum)
            {
                throw new ArgumentException("Type provided must be an Enum.", "T");
            }

            Array enumValues = Enum.GetValues(type);

            foreach (Enum enumVal in enumValues)
            {
                if (GetDescription(enumVal).CompareTo(value) == 0)
                    return (T)Enum.Parse(type, enumVal.ToString(), ignoreCase);
            }
            throw new ArgumentException("Must specify a valid Enum description value.", "value");
        }

 

These parsing methods aren't my creation.