Enterprise Library 2.0 Logging Application Block Part III - Programmatically Using Logging Application Block With No Configuration File

Enterprise Library 2.0 Logging Application Block Part III - Programmatically Using Logging Application Block With No Configuration File

by David Hayden ( Florida .NET Developer )

 

This is Part III in a series of tutorials on the Enterprise Library 2.0 Logging Application Block.  The first two parts were:

 

In this example, I will show one how to programmatically use the Enterprise Library 2.0 Logging Application Block without a configuration file.

Before I begin, special thanks goes to Alois Kraus who has written a similar article, called Programatic Configuraton - Enterprise Library (v2.0) Logging Block.  While I was learning this myself, Alois came out with his very timely article that cleared up a number of questions.

 

Enterprise Library 2.0 Logging Application Block Key Objects

There are several key objects that you need to familiar yourself with before you go any furthere

  1. LogFormatter - typically a TextFormatter or BinaryFormatter. The role of formatters in the Logging Application Block is to specify the way you want your log messages to look in the intended datastore ( file, database, email, etc. ).  Most of the time you will use the TextFormatter, which has a Template Property that specifies the “template“ of the message.  Inside this template you can have a number of tokens ( e.g. {timestamp} ) that the Logging Application Block will fill for you.
  2. TraceListener - As mentioned in Part I, tracelisteners provide the logging services.  They are the conduit for which messages make it to their intended destinations ( database, email, flat file, event log ).  The Logging Application Block comes with several tracelisteners such as:  Database TraceListener, Email TraceListener, Flat File TraceListener, Formatter Event Log TraceListener , etc.  The TraceListener requires a Formatter to specify the message format.
  3. LogSource - LogSource is a collection of TraceListeners that work as a unit.
  4. LogWriter - This is The Man.  The LogWriter is the focal point of the Logging Application Block.  It is the glue that binds all the tracelisteners, filters, and everything else together to make sure the messages that you log and pass through the filters get to the respective tracelisteners.  It is the LogWriter.Write(LogEntry log) method that makes it all happen.
  5. LogEntry - This is the class that holds your message.  You will set the Message, Category, Priority, EventId, Severity, and other properties on this class for logging.  You may not knowingly use the class directly, because often classes in the Logging Application Block may fill it out for you.  However, this is the class that ultimately gets passed to the LogWriter that pushes the message through to the intended LogSource and its TraceListeners.
  6. ILogFilter - I will talk about this in another post, but utimately it is a collection of objects implementing the ILogFilter Interface that determine if a message actually gets logged by the LogWriter.  It could be a CategoryFilter, LogEnabledFilter, PriorityFilter, or a custom filter you built yourself.

 

Why Use Logging Application Block Objects Directly

A good question is why one would use the Logging Application Block objects directly as opposed to using the facade and factory classes that work with an IConfigurationSource?  My opinion is that you will gain more speed by losing all the configuration overhead.  I haven't measured the difference so it may be negligible in the real world, but certainly there has to be some overhead of using the facade and factory classes.

The main drawback about using the objects directly is the ability to make changes in how, when, and where messages are logged without touching code.  The beauty of the Logging Application Block is that one can modify the web.config or app.config to control logging without recompiling the application.  I can add filters, new tracelisteners, etc. on the fly via the configuration file.  If your needs are simple and will never change, this is no big deal.  But if you see logging as a moving target, better to encapsulate it using the built in facade.

 

Enterprise Library 2.0 Logging Application Block Sample

Shown below is an example of programmatically using the Logging Application Block through code without the use of a configuration file.  Note that this is very similar to Alois Kraus' example as his article finally helped me understand the true inner-working of the Logging Application Block.  Per some comments on Alois' article, I chose to handle thread safety using a static constructor.  It is a minor change, but I felt it made the MyLogger Class much more simple to comprehend.  I checked with Alois and he gave me a thumbs up on the simplification :)

I have commented the code pretty heavily to guide you through the process, which hopefully will answers any questions on it.

 

/// <summary>
/// MyLogger logs messages in the application.
/// </summary>
public static class MyLogger
{
    static readonly LogWriter _writer;

    static MyLogger()
    {
        // The formatter is responsible for the
        // look of the message. Notice the tokens:
        // {timestamp}, {newline}, {message}, {category}
        TextFormatter formatter = new TextFormatter
            ("Timestamp: {timestamp}{newline}" +
            "Message: {message}{newline}" +
            "Category: {category}{newline}");


        // Log messages to a log file.
        // Use the formatter mentioned above
        // as well as the header and footer
        // specified.
        FlatFileTraceListener logFileListener =
            new FlatFileTraceListener("c:\\messages.log",
                                       "----------", 
                                       "----------",
                                       formatter);


        // My collection of TraceListeners.
        // I am only using one.  Could add more.
        LogSource mainLogSource =
            new LogSource("MainLogSource", SourceLevels.All);
        mainLogSource.Listeners.Add(logFileListener);


        // Assigning a non-existant LogSource
        // for Logging Application Block
        // Specials Sources I don't care about.
        // Used to say "don't log".
        LogSource nonExistantLogSource = new LogSource("Empty");


        // I want all messages with a category of
        // "Error" or "Debug" to get distributed
        // to all TraceListeners in my mainLogSource.
        IDictionary<string, LogSource> traceSources =
new Dictionary<string, LogSource>(); traceSources.Add("Error", mainLogSource); traceSources.Add("Debug", mainLogSource); // Let's glue it all together. // No filters at this time. // I won't log a couple of the Special // Sources: All Events and Events not // using "Error" or "Debug" categories. _writer = new LogWriter(new ILogFilter[0], traceSources, nonExistantLogSource, nonExistantLogSource, mainLogSource, "Error", false, true); } /// <summary> /// Writes an Error to the log. /// </summary> /// <param name="message">Error Message</param> public static void Write(string message) { Write(message, "Error"); } /// <summary> /// Writes a message to the log using the specified /// category. /// </summary> /// <param name="message"></param> /// <param name="category"></param> public static void Write(string message, string category) { LogEntry entry = new LogEntry(); entry.Categories.Add(category); entry.Message = message; _writer.Write(entry); } }

 

It perhaps is dangerous to map all of the Logging Application Block's Special Sources to a non-existant LogSource, such as the nonExistantLogSource.  I could see not logging the All Events Special Source if you want more control of messages using Categories.  However, as I did above, I would recommend logging at least any warnings and error messages emitted from the Logging and Error Warnings Special Source.  These are errors and warnings coming from the Logging Application Block itself allowing you to monitor its health.

 

Testing the Class

I put together a quick web application to test out the new class.  Here is the extent of the code:

 

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        MyLogger.Write("My Error");
        MyLogger.Write("My Debug", "Debug");
    }
}

 

Pretty simple, eh :)  The output is pretty simple, too:

 

----------
Timestamp: 2/18/2006 6:24:49 PM
Message: My Error
Category: Error

----------
----------
Timestamp: 2/18/2006 6:24:49 PM
Message: My Debug
Category: Debug

----------

 

Conclusion

Hopefully this example provides a much clearer understanding of the Logging Application Block and how you can use it programmatically to avoid configuration files.  Certainly you can use it in this way if your needs are simple and you are concerned with the performance overhead of configuration.  If in doubt, I would personally always use the nice Facade class that keeps you from having to think this much :)

By the way, I will be giving a presentation at the Orlando Code Camp on Enterprise Library 2.0.  I will be diving into into every single application block in detail with many examples on using the facades, factory classes, and objects directly.  I will also be talking about Enterprise Library 2.0 common design patterns, OOP, and use of best practices.  I hope to see you there.

 

Source:  David HaydenFlorida .NET Developer )

 

Enterprise Library 2.0 Tutorials

 

posted on Saturday, February 18, 2006 2:12 PM

Main

News

Green Tea

.NET Development

Enterprise Library

Patterns & Practices