Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Enterprise Library – Logging and Instrumentation Application Block – Patterns and Practices

Another useful application block that is part of the Enterprise Library is the Logging and Instrumentation Application Block.  It allows you to log events in your applications to various locations, such as a database, the event log, email, a flat file, etc.


As you can see from the snapshot of the configuration tool below, I created a single location to store all my events – a flat file.  It is the MessageLogSink, which stores messages in a file I decided to call message.log.


 


Logging Application Block


 


For testing purposes, I decide to group all my events into 5 categories:



  • Debug
  • Exception
  • General
  • Performance
  • Security

You have to assign one of the categories as default, so I assigned General as default.  Each category can be assigned to one of more sinks.  Thus you could have events of type Exception not only stored to a text file, but also sent to you via email, placed in the event log, etc.  In my case, I just assigned them all to MessageLogSink that saves messages to message.log.


How you organize your categories is fairly important because you not only have the ability to channel categories to one or more different locations, but you also have the ability to filter on categories.  For example, when you first start building the application you will probably want to see all events of type Debug.  However, when the application gets closer to production or into production, you will probably want to turn off a lot of the Debug messages.  You can also filter on Priority as well, so one needs to think out the strategy on how you organize the events and the priority of the events within each category so that you can minimize the logging as the application becomes more mature.


The Logging and Instrumentation Application Block is called within your application using a simple Logger.Write(…) call that has several overloads where you can pass parameters:


 


Logger.Write
public static void Write(object message)
public static void Write(object message, string category)

public static void Write(LogEntry log)

 


The parameters are as such:



  • Message
  • Category
  • Priority
  • Title
  • EventID
  • Severity
  • etc…

These values are all neatly placed in a type of LogEntry that gets distributed (after filtering) to each Sink on a category-by-category basis via the Interface ILogSink.


 


ILogSink Interface
public interface ILogSink
{
/// <summary>
/// Send message to log sink handler.
/// </summary>
/// <param name=”log”>xmlMessage as string</param>
void SendMessage(LogEntry log);

/// <summary>
/// Sets the formatter to be used by this sink.
/// </summary>

    ILogFormatter Formatter { get; set; }
}

 


The formatter you see mentioned above is also configurable per sink and category and just specifies what you want the message to look like and what information you want it to contain.


After I finished creating the configuration files, I created a quick test application in Snippet Compiler as shown below.  To stay somewhat consistent on how I use the categories and set priorties, etc., I derived more specific category-based LogEntry classes from LogEntry.  This is totally unnecessary as I was just playing:


 


Testing Logging Application Block
using System;
using System.Collections;

using Microsoft.Practices.EnterpriseLibrary.Logging;

public class GeneralLogEntry : LogEntry
{
public GeneralLogEntry(string message) : this(message, 2) {}

public GeneralLogEntry(string message, int priority) : base()
{
Category
= General;
Priority
= priority;
Severity
= Severity.Unspecified;
Message
= message;
}
}

public class ExceptionLogEntry : LogEntry
{
public ExceptionLogEntry(string message) : this(message, 5) {}

public ExceptionLogEntry(string message, int priority) : base()
{
Category
= Exception;
Priority
= priority;
Severity
= Severity.Error;
Message
= message;
}
}

public class PerformanceLogEntry : LogEntry
{
public PerformanceLogEntry(string message) : this(message, 3) {}

public PerformanceLogEntry(string message, int priority) : base()
{
Category
= Performance;
Priority
= priority;
Severity
= Severity.Information;
Message
= message;
}
}

public class SecurityLogEntry : LogEntry
{
public SecurityLogEntry(string message) : this(message, 5) {}

public SecurityLogEntry(string message, int priority) : base()
{
Category
= Security;
Priority
= priority;
Severity
= Severity.Warning;
Message
= message;
}
}

public class DebugLogEntry : LogEntry
{
public DebugLogEntry(string message) : this(message, 1) {}

public DebugLogEntry(string message, int priority) : base()
{
Category
= Debug;
Priority
= priority;
Severity
= Severity.Information;
Message
= message;
}
}

public class MyClass
{
public static void Main()
{
Logger.Write(
new GeneralLogEntry(A Beautiful Day));
Logger.Write(
new DebugLogEntry(Loading Array));
Logger.Write(
new SecurityLogEntry(User is not authenticated.));
Logger.Write(
new PerformanceLogEntry(Application running slow…, 4));
Logger.Write(
new ExceptionLogEntry(Division by zero exception.));
}

}


 


Here is the output using my template for Text Formatter:


 


Messages.log
—————————————-
Timestamp:
3/16/2005 4:50:49 PM
Message: A Beautiful Day
Category: General
Priority:
2
Severity: Unspecified
—————————————-
Timestamp:
3/16/2005 4:50:50 PM
Message: Loading Array
Category: Debug
Priority:
1
Severity: Information
—————————————-
Timestamp:
3/16/2005 4:50:50 PM
Message: User
is not authenticated.
Category: Security
Priority:
5
Severity: Warning
—————————————-
Timestamp:
3/16/2005 4:50:50 PM
Message: Application running slow…
Category: Performance
Priority:
4
Severity: Information
—————————————-
Timestamp:
3/16/2005 4:50:50 PM
Message: Division by zero exception.
Category: Exception
Priority:
5
Severity: Error

 


You could also pull off a cleaner and more consistent model by using your own facade on top of Logger to maintain the additional consistency in your applications such as:


 


Log Facade
using System;
using System.Collections;

using Microsoft.Practices.EnterpriseLibrary.Logging;

public enum Priority : int
{ Low
= 1, Medium = 3, High = 5 }

public sealed class Log
{
public static void WriteGeneralEntry (string message, Priority priority)
{
Logger.Write(message,
General, (int)priority);
}

public static void WriteDebugEntry (string message, Priority priority)
{
Logger.Write(message,
Debug, (int)priority);
}
}

public class MyClass
{
public static void Main()
{
Log.WriteGeneralEntry(
New Entry, Priority.Low);
Log.WriteDebugEntry(
Blog Down, Priority.High);
}

}


 


You could obviously refactor the code above as necessary, but you get the idea.  I am basically trying to create a more intuitive method and consistent manner in which I use the Logging and Instrumentation Application Block so that as I grow from an immature application to a mature application I can filter the amount of information that is written into the log as necessary by Category and Priority.


I have purposely not posted the config files with this example as they are your typical Enterprise Library log files.


Here are other Enterprise Library Application Blocks that I have posted about:



You can download Enterprise Library here.


Don’t forget about the Enterprise Library webcasts.

This entry was posted in Application Blocks. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

5 Responses to Enterprise Library – Logging and Instrumentation Application Block – Patterns and Practices

  1. David Hayden says:

    Tom Hollander talks about the Logging Application Block vs. Log4Net "debate" here:

    http://blogs.msdn.com/tomholl/archive/2005/03/15/396189.aspx

    He also mentions as I did in the post that they are looking at ways to speed up the performance now. Fantastic. They focus on a solid tool and then look at ways to improve it in subsequent releases.

    He also doesn’t dismiss the fact that Log4Net is perhaps a faster and simpler tool. If you just need a logging tool with simple needs, it is hard to beat a tool like Log4Net that just does logging. The big picture is the suite of tools you get from EntLib. It will no doubt be overkill for many applications, and for some it will be a "perfect" solution.

    Nicely said, Tom.

  2. David Hayden says:

    Thanks for the link, Patrik.

    I have been using Log4Net for all my application logging and even though a new version hasn’t been released in some time, it has worked flawlessly for me. Log4Net is an excellent logging solution.

    I haven’t compared the performance, but I am sure that most (if not all) of the applications blocks in Enterprise Library will be slower than a more dedicated counterpart. This is due to the fact that the application blocks are instantiating types on the fly using reflection after reading the type information from a configuration file. And because to the best of my knowledge I don’t see any caching being done in the application blocks, this occurs everytime you ask for the CacheManager in the Caching Application Block, get an instance of Database in the DAAB, do a Logger.Write in the Logging Block, etc.

    I don’t believe the Patterns and Practices Group were focused on writing high-performance blocks on the first version. They were probably more focused on writing a suite of solutions that worked together in a common fashion and were highly extensible.

    Loren’s post is not as balanced as I would ideally prefer. He mentions minor things (as he even said) about priority not being an Enumeration, and I just "solved" it here in my example :) These kinds of strategies are not necessarily required of the logging infrastructure, but required of your application architecture.

    I will take a stab at a similar post in the near future as I think this is a great discussion. Thanks for pointing out Loren’s post.

  3. Patrik says:

    This is interesting reading:

    http://weblogs.asp.net/lorenh/archive/2005/02/18/376191.aspx

    They compare the speed of Logging block and Log4Net.

  4. David Hayden says:

    The Logging Application Block requires the Configuration Application Block only to read the logging configuration settings. In fact, every application block requires the configuration application block to read its configuration settings.

    This is transparent to you. It just means you have to add the extra configuration DLL to your bin directory and that is it. You can still use web.config for your application settings.

  5. Jun Meng says:

    Hi,

    Maybe I missed something good of the new Enterprise library. For web application, if I want to use the Log library ONLY, how can I do that?

    It seems Log library depends on Configuration library that I do not need (as I have web.config already). I feel confused that what’s the use for the new library to combine those blocks together in a web application.

    Any suggestions? Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>