Click here to Skip to main content
Click here to Skip to main content

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

By , 15 Jul 2004
 

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>
        /// <PARAM name="obj">The LogMessage object</PARAM>
        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>
        /// <PARAM name="objMessage"></PARAM>
        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

About the Author

sebma
Web Developer
Singapore Singapore
Member
A software craftman, the roads I travelled include C++ to Java to C#, from Windows to Unix and now Microsoft .NET.
Full-time, I play the pragmatic roles of software designer, engineer and architect in the programming trenches.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionmissing code to access remote queue ?memberyoavmorag@hotmail.com15 Jun '06 - 4:25 
I had to add the following code in GetPrivateQueue in order to access a remote queue:
MessageQueue rq = new MessageQueue(strMsmqPath);
return rq;
(after the loop looking for local - could replace it as well I guess)
had also to add using System.Messeging.
 
Yoav
GeneralStop MSMQ observermemberrickie12319 Nov '04 - 21:09 
At first, thanks for your articles on AppLogger.
*
Your demo code to stop MSMQ observer
public void Terminate()
{
if (m_blnRunning)
{
m_blnRunning = false;
// m_objMsmq2Listen.Close();
}
}
Confused | :confused: I don't know why m_objMsmq2Listen observer can stop because the above code does not handle m_objMsmq2Listen object, only change variable m_blnRunning to be false.
 
Thanks for you help in advance.
GeneralRe: Stop MSMQ observermembersebma7 Dec '04 - 18:59 
Was away from office, sorry for the late reply.
 
The Terminate() method is redundant and you can remove it. The queue would still be receiving messages even when AppLogger is not running.
 
However, there will be a high surge in CPU when a MSMQ receiver(AppLogger in our case) starts running while there are thousands/millions of messages waiting to be processed. Under such circumstances, it may be appropriate for the message processor to pause for a configurable interval (eg. sleep(5000) after processing 1000 messages).
 
This is the reason why AppLogger should be deployed as a Windows Service and should always be the first to run and last to shutdown.
 
Cheers Wink | ;)
-Seb
 
The Pragmatic Programmer
GeneralRe: Stop MSMQ observermemberrickie1238 Dec '04 - 7:41 
Thank you, Seb. Smile | :)
GeneralRunning v2 without MSMQmembermichael_e_obrien17 Sep '04 - 5:09 
Just to let everyone know ...
 
If you remove the MSMQ line in the config, you will need to modify the code slightly to check for nulls. This will let the application run and default back to the remoting interface. Without null checks, the AppLogger throws several exceptions, and will not run.
 
I did this to allow for the flexibility of switching between the 2 different interfaces. Works great now.
GeneralChanges for Dist SystemsussH JOSHI15 Aug '04 - 14:06 
Hi,
 
Very good article.
 
I have a server that is not part of a domain running Adv Server and a client machine (also not part of a domain) running XP Pro.
What changes do I need to make if I want to run your client on the XP machine and the Logger (server) on the Windows AServer machine?
 
Today, when I run the client as it is from the XP, I get the message "Queue Not Found in MSMQ".
 
ALso, how would I restrict the access to the Private queue to a limited users on limited sets of machines?
 

Thanks.
GeneralRe: Changes for Dist SystemsussAnonymous19 Aug '04 - 0:43 

 
Noticed that the client machine was "hard coded". Changed that to get the m/c name and works fine as it is accross the network.
 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 16 Jul 2004
Article Copyright 2004 by sebma
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid