Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

AppLogger, a Simple Distributed Application Logger - Part 2 (Using MSMQ)

0.00/5 (No votes)
15 Jul 2004 1  
Concluding article for simple distributed application logging

Figure 1. Computer Management console screen-shot

Introduction

A quick-and-easy distributed application logger (AppLogger) was introduced in Part 1 of this article. TCP Remoting was used as the underlying protocol that makes AppLogger "distributed". The use of BusinessFacade, BusinessRules, DataAccess and SystemFrameworks layers was also presented as a way to structure your application. It was also mentioned that logging should be a short-and-fast action, i.e. the caller should never be in a blocking state. "Asynchronization" was implemented server-side in AppLogger's BusinessRule layer.

In this article, we will "refactor" AppLogger to make it even more "asynchronous". In fact, we want to make the client-side calls (via AppLoggerHelper APIs) to be asynchronous via some sort of one-way messaging mechanism. Again, instead of creating a message queuing mechanism from scratch, we will apply some pragmatism. We will utilize the MSMQ provided by the Operating System(Windows XP, Windows 2000, Windows 2003) itself. Note that MSMQ is not automatically activated when you install your OS. You will have to add it yourself in "Control Panel->Add/Remove Programs->Add/Remove Windows Components".

To run the demo, just unzip the demo files, and do the following:

  • from Control Panel->Administrative Tools->Computer Management, select Message Queuing, then add a Private Queue called "applogger". (Refer to Figure 1 above)
  • start \AppLoggerDemo2\AppLogger\AppLogger.exe
  • then run \AppLoggerDemo2\AppLoggerTest\AppLoggerTest.exe

Background

In an ideal world, application error logging should be an exception and not the norm. If there are so many errors and exceptions to log, your applications are already functioning in degraded mode. However, this world is less than ideal (as we all know it), and more often than not we even need to log debug messages just to keep us happy that things are running fine.

So in order not to "degrade" our AppLogger, we want to make the APIs provided by AppLoggerHelper asynchronous. In essence, users of AppLoggerHelper will effectively "enqueue" their log messages and AppLogger will "dequeue" and forward them to the concrete IAppLogger. As usual, we will touch base with a few design patterns along the way.

Features

The following has been added to the projects in AppLogger solution:

BusinessFacade

Reference to System.Messaging.dll added.

MsmqHelper class added.
ProxyMsmqHome class added.
ProxyMsmqLogger class added.
BusinessRules

Reference to System.Messaging.dll added.

MsmqObserver class added.
MsmqLogger class added.

The MsmqHelper class provides helper methods to open a queue from MSMQ. The ProxyMsmqHome (a concrete IHome) and ProxyMsmqLogger (a concrete IAppLogger), as the name implied, is nothing but a Proxy classes. "The Proxy design pattern makes the clients of a component communicate with a representative rather than the component itself" - Frank Buschmann et. al., from their book "Pattern-Oriented Software Architecture" (otherwise known as the POSA book). As it turns out, MSMQ is just another "transport" protocol we need to set in our App.Config files for both client(AppLoggerTest.exe) and server(AppLogger.exe).

AppLogger.exe Configuration

AppLogger.exe's App.Config has a new key call "AppLogger.MSMQ". Note that "bordeaux" is my machine name, change it to your machine name (not necessarily "chablis", just joking...).

    <appSettings>
        <!--
            AppLogger.Type allows changing the type 
            of logger during runtime. Eg. ConsoleLogger, 
            FileLogger, EventLogger, DbLogger, DebugLogger
        -->
        <add key="AppLogger.Type"
          value="AppLogger.BusinessRules.ConsoleLogger" >
        ...
        <!--
            AppLogger.MSMQ is for MsmqLogger only. 
            Private queue "applogger" must exists in local machine. 
            Client side must have the following in <appSettings> 
            <add key="AppLogger.Home.Location" 
            value="msmq:FormatName:DIRECT=OS:<hostname>\private$\applogger" />
            Server side, which is this App.config, 
            must have the following in <appSettings> 
            <add key="AppLogger.MSMQ" 
            value="FormatName:DIRECT=OS:<hostname>\private$\applogger" />
        -->
        <add key="AppLogger.MSMQ" 
        value="FormatName:DIRECT=OS:bordeaux\private$\applogger" />
    </appSettings>

Client Configuration

In AppLoggerTest's App.Config, only the value of the key "AppLogger.Home.Location" needs to be changed to using "msmq:" protocol.

    <appSettings>
        <!--
            AppLogger.Home.Location refers to the URL of AppLogger's IHome
            <add key="AppLogger.Home.Location" 
              value="tcp://localhost:20911/AppLogger.IHome.rem" />
            OR via MSMQ
            <add key="AppLogger.Home.Location" 
              value="msmq:FormatName:DIRECT=OS:<hostname>\private$\applogger" />
            -->
        <add key="AppLogger.Home.Location" 
          value="msmq:FormatName:DIRECT=OS:bordeaux\private$\applogger" />
        ...
    </appSettings>

Using the code

MsmqObserver and MsmqLogger Classes

As the name implies, MsmqObserver observes for (or listens to) messages being sent to the physical "applogger" queue in MSMQ. The MsmqLogger, which is a Singleton and also implements the IAppLogger interface, will subscribe for notifications (via callback method MsmqLogger.NotifyMe(...)) with MsmqObserver. In short, MsmqLogger is asking MsmqObserver to notify it when a message is received from MSMQ. This typical Observer pattern is well explained in Gang-Of-Four's "Design Patterns" book. The MsmqLogger Singleton is started when AppLogger.exe starts up.

At this junction, it is good to point out that before the jargon "Design Patterns" became common-speak, there is such a thing call "Idioms", phrased by James O. Coplien, author of a highly-rated book "Advanced C++ Programming Styles and Idioms". The MsmqLogger, upon notification of log message arrival, forwards the message to the real logger (the real logger is an already implemented concrete IAppLogger that knows where to persist the log message to). This typifies the "Handle/Body" idiom or so-called Bridge design pattern.

    public class MsmqLogger : IAppLogger
    {
        /// <summary>

        /// Holds this singleton, with eager initialization.

        /// </summary>

        private static MsmqLogger m_objInstance = new MsmqLogger();
        /// <summary>

        /// Holds our message Observer.

        /// </summary>

        private MsmqObserver m_objMsmqObserver;
        /// <summary>

        /// Holds the real logger used for logging.

        /// </summary>

        private IAppLogger m_objRealLogger;

        /// <summary>

        /// Not for public construction.

        /// </summary>

        private MsmqLogger()
        {
            Setup();
        }

        /// <summary>

        /// Initializes class members.

        /// </summary>

        private void Setup()
        {
        ...not shown for simplicity...
        }

        /// <summary>

        /// This is the callback method for MsmqObserver.

        /// </summary>

        /// The LogMessage object

        private void NotifyMe(object obj)
        {
            System.Messaging.Message objRawMsg = (System.Messaging.Message)obj; 
            AppLogger.BusinessFacade.LogMessage objActualMsg = 
                (AppLogger.BusinessFacade.LogMessage)objRawMsg.Body;

            LogMessage(objActualMsg); // forwarding to real logger


            objActualMsg = null;
            objRawMsg = null;
        }

        /// <summary>

        /// Get this singleton.

        /// </summary>

        /// <returns>A MsmqLogger</returns>

        public static MsmqLogger GetInstance()
        {
            return m_objInstance;
        }

        /// <summary>

        /// IAppLoggger.LogMessage Implementation.

        /// </summary>

        /// 

        public void LogMessage(LogMessage objMessage)
        {
            // forwarding to the real logger

            m_objRealLogger.LogMessage(objMessage);
        }

        ...

    }   

And finally, thanks to the Proxy pattern, there is no client-side code changes in AppLoggerTest.

Conclusion

This poor man's distributed application logger can be deployed at multiple application servers. However, in the real world, it is quite a nightmare to keep multiple error log files or to search through multiple Windows Event logs in multiple machines. As mentioned in Part 1 of this article, you can certainly provide a DbLogger (in BusinessRules and DataAccess layers) which persists log messages to your database. Once stored in database, tracing, searching and filtering will just be a matter of SQL statements.

It seems that this "economy-class" logging application had taken us not only to design patterns lands, but also accommodated our refactored designs. I hope you enjoy reading as much as I enjoy writing this article. Again, keep your suggestions flowing and Rock On!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here