Figure 1. Computer Management console screen-shot
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
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.
The following has been added to the projects in AppLogger solution:
Reference to System.Messaging.dll added.
MsmqHelper class added.
ProxyMsmqHome class added.
ProxyMsmqLogger class added.
Reference to System.Messaging.dll added.
MsmqObserver class added.
MsmqLogger class added.
MsmqHelper class provides helper methods to open a queue from MSMQ. The
ProxyMsmqHome (a concrete
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'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...).
In AppLoggerTest's App.Config, only the value of the key "AppLogger.Home.Location" needs to be changed to using "msmq:" protocol.
Using the code
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
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
private static MsmqLogger m_objInstance = new MsmqLogger();
private MsmqObserver m_objMsmqObserver;
private IAppLogger m_objRealLogger;
private void Setup()
...not shown for simplicity...
public static MsmqLogger GetInstance()
And finally, thanks to the Proxy pattern, there is no client-side code changes in AppLoggerTest.
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!
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.