Using log listeners

When you perform actions using the ADAM commandline tools, you will see output in your commandline window. Some of the info written there is also sent to the Windows Event Log. We do that using the following line of code:

C#
1
Adam.Tools.LogHandler.LogManager.Write(LogSeverity.Warning, "some message");

Did you notice how we write to the commandline and to the Windows Event Log with one single call? Did you also notice we didn't tell the LogManager where to write the message? Actualy, that's not entirely true. We tell the LogManager where to write the message in the App.Config of our applications. In most cases we use the following XML to setup our LogManager.

XML
1
2
3
4
5
6
7
8
9
10
 <configSections>
  <section name="Adam.Core" type="Adam.Core.ConfigurationHandler, Adam.Core, Version=4.5.0.0, Culture=neutral, PublicKeyToken=63f11f167f68d05b" />
  <section name="Adam.Tools" type="Adam.Tools.ConfigurationHandler, Adam.Tools, Version=4.5.0.0, Culture=neutral, PublicKeyToken=63f11f167f68d05b" />
 </configSections>
 <Adam.Tools>
  <logListeners>
   <add type="Adam.Tools.LogHandler.ConsoleLogListener,Adam.Tools" severity="Normal" displayAdditionalInfo="false" displayStackTrace="false"/>
   <add type="Adam.Tools.LogHandler.EventLogListener,Adam.Tools" severity="Normal"/>
  </logListeners>
 </Adam.Tools>

The add tags define two log listeners. The first one is of type ConsoleLogListener that will write all output to the console. The second one is of type EventLogListener and will write to the Windows Event Log.

But what if we would like to output the LogManager to a text file. Well then you're in luck, there is a TextFileLogListener you can use or add to your App.Config. It gets harder when you want to write to an XML file or if you want to use colors to accent the console output. As always, ADAM provides you with the ability to write your own log listener. For this post, I have created a LogListener that will change the color of your console output depending on the LogSeverity of the info that is logged.

Test application

We start by creating a console application. This console application will allow us to test the LogManager. The source code of the Program class looks like this:

C#
1
2
3
4
5
6
7
8
static void Main()
{
 foreach (LogSeverity severity in Enum.GetValues(typeof(LogSeverity)))
 {
  string message = string.Format("Some message with severity set to '{0}'.", severity);
  LogManager.Write(severity, message);
 }
}

If you would use the App.Config above with this code, you will get an output that looks like this:

15:27:51:787 0 (None): Some message with severity set to 'None'.
15:27:51:790 0 (Error): Some message with severity set to 'Error'.
15:27:51:791 0 (Warning): Some message with severity set to 'Warning'.
15:27:51:791 0 (Normal): Some message with severity set to 'Normal'.

Moving to the ColoredConsoleLogListener

We want the same output as above, but we want errors to be colored red, warnings to be yellow and normal to be green. We start by changing our App.config file to this:

XML
1
2
3
4
5
6
7
8
9
<Adam.Tools>
 <logListeners>
  <add type="Adam.Blog.LogListenerTest.ColoredConsoleLogListener, Adam.Blog.LogListenerTest" severity="Normal">
   <color consoleColor="Red" severity="Error" />
   <color consoleColor="Yellow" severity="Warning" />
   <color consoleColor="Green" severity="Normal" />
  </add>
 </logListeners>
</Adam.Tools>

The configuration XML tells us which color should be used for what severity.

Now we have the config, we will start coding. Create a class called ColoredConsoleLogListener. Since we want it to work just like the ConsoleLogListener, only with colors, you should have the class inherit ConsoleLogListener. Now lets start creating the constructor. The constructor takes an XmlNode as parameter. The value of that XmlNode will be the add node of the App.config.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
private static readonly Dictionary<LogSeverity, ConsoleColor> _ColorSettings = new Dictionary<LogSeverity, ConsoleColor>();
private const string _SeverityAttributeName = "severity";
private const string _ColorAttributeName = "consoleColor";
private const string _ColorNodeName = "color";

public ColoredConsoleLogListener(XmlNode node) // node is the add node of App.Config
 : base(node)
{
 // getting the child nodes named color
 XmlNodeList colorSettings = node.SelectNodes(_ColorNodeName);
 foreach (XmlNode colorSetting in colorSettings)
 {
  // read the value of the consoleColor attribute from the color node
  ConsoleColor? outputColor = GetEnumValueFromAttribute<ConsoleColor>(colorSetting, _ColorAttributeName);
  if (outputColor == null)
  {
   continue;
  }

  // read the value of the severity attribute from the color node
  LogSeverity? severity = GetEnumValueFromAttribute<LogSeverity>(colorSetting, _SeverityAttributeName);
  if (severity == null)
  {
   continue;
  }
  
  // add it to the dictionary so we can call upon them later
  _ColorSettings.Add(severity.Value, outputColor.Value);
 }
}

// just a method to read an Enum value from an attribute
private T? GetEnumValueFromAttribute<T>(XmlNode nodeOfAttribute, string attributeName)
 where T : struct 
{
 XmlAttribute attribute = nodeOfAttribute.Attributes[attributeName];
 if (attribute == null)
 {
  return null;
 }

 T enumValue;
 if (!EnumConverter.TryParse(attribute.Value, true, out enumValue))
 {
  return null;
 }
 return enumValue;
}

The constructor will be called sometime between the start of your application and the first time the LogManager is called, but we still will not get our output in color. For that we need to override the Write method.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public override void Write(LogSeverity severity, int eventId, string category, string message, Exception ex, IDictionary<string, string> additionalInfo)
{
 // Remembering the original color so we can restore that color
 ConsoleColor originalForegroundColor = Console.ForegroundColor;
 
 // Looking if we have a color for the severity
 if (_ColorSettings.ContainsKey(severity))
 {
  // Setting the new color
  Console.ForegroundColor = _ColorSettings[severity];
 }
 try
 {
  // Calling the ConsoleLogListener
  base.Write(severity, eventId, category, message, ex, additionalInfo);
 }
 finally
 {
  // Even when base.Write fails, we will restore the original color
  Console.ForegroundColor = originalForegroundColor;
 }
}

And that is all there is to it.

Sample Code

The article contains sample code project(s).
You must be logged in to view or download sample code.
Sign in now

Comments

Leave a comment
You must be logged in to post comments.
Sign in now
 
 
Technical
Business
rss feed