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

AppLogger, a Simple Distributed Application Logger - Part 1

0.00/5 (No votes)
6 Jul 2004 1  
An article on distributed application logging

Figure 1. DebugView

Figure 2. EventViewer

Figure 3. Log files

Figure 4. Console

Introduction

This article presents a quick-and-easy distributed application logger (known as AppLogger), without all the frills and overheads that come with Microsoft's Exception Management and Logging Application Blocks.

To run the demo, just unzip the demo files,

  • start \AppLoggerDemo\AppLogger\AppLogger.exe
  • then run \AppLoggerDemo\AppLoggerTest\AppLoggerTest.exe

Background

One of the most basic and fundamental component for any distributed system is an application logger providing a networked logging service in which the system's collaborating components can log errors or exceptions to.

Douglas C. Schmidt, a renown authority in real-time distributed systems (especially in CORBA), presented a Networked Logging Service in his book "C++ Network Programming, Volume 1, Mastering Complexity with ACE and Patterns".

Needless to say, .NET Remoting (a close technology cousin to CORBA), is used in the AppLogger solution presented in this article.

Features

AppLogger.exe is built in the solution's SystemFrameworks project. By default, the project is built as console application to facilitate debugging. To build it as Windows Service, all you need to do is to switch the project's properties' "Start Object" to "AppLogger.SystemFrameworks.AppAsService".

All built binaries are output to Assemblies project's /bin folder. I have to warn you that there exists a Microsoft bug in which Visual Studio.NET 2003 is unable to copy or save files to a common folder if your solution/projects get too big. But AppLogger is a small application, so you will not hit this bug.

Remoting Server Configuration

AppLogger.exe's Remoting configuration(in App.Config) is set to "SingleCall", TCP mode using port 20911. Its actual runtime logger is instantiated on startup, and can be changed using the <appSettings> section. The four types of logger which you can configure are:

DebugLogger Logs output to DebugView (a freeware available from http://www.sysinternals.com/ since the days of Win32 and MFC). Refer to Figure 1 above.
EventLogger Logs output to Windows Event Viewer. Refer to Figure 2 above.
FileLogger Logs output to text file(s). Each source application has its own file. Refer to Figure 3 above.
ConsoleLogger Logs output to console. Refer to Figure 4 above.
DbLogger Not implemented in this demo, but you can easily implement an ADO.NET class under DataAccess project that logs output to database.

<appSettings>
    <!--
     AppLogger.Type allows changing the type of logger during 
     runtime. Eg. ConsoleLogger, FileLogger, EventLogger, 
     DbLogger, DebugLogger
    -->
    <add key="AppLogger.Type" value="AppLogger.BusinessRules.FileLogger" >
    ...
</appSettings>

Remoting Client Configuration

AppLoggerTest project is our test client and it only needs to have the following configuration in its App.Config.

    <appSettings>
        <!--
            AppLogger.Home.Location refers to the URL of AppLogger's IHome
            -->
        <add key="AppLogger.Home.Location" 
          value="tcp://localhost:20911/AppLogger.IHome.rem" />
        ...
    </appSettings>

Using the code

AppLoggerHelper Class

The AppLoggerHelper is a wrapper class in AppLogger's BusinessFacade project. It provides an easy API for logging exception, error, warning, information or debug messages. It hides the Remoting and log message complexity from the caller.

    public class AppLoggerHelper
    {
        /// <SUMMARY>

        /// Holds the url location of AppLogger

        /// </SUMMARY>

        private static string s_strLocationHome = 
          ConfigurationSettings.AppSettings["AppLogger.Home.Location"];

        /// <SUMMARY>

        /// Not to be instantiated.

        /// </SUMMARY>

        private AppLoggerHelper()
        {
        }

        /// <SUMMARY>

        /// Logs an exception message.

        /// </SUMMARY>

        public static void LogException(Exception ex)
        {
        ...not shown for simplicity...
        }
        
        /// <SUMMARY>

        /// Logs an error message.

        /// </SUMMARY>

        public static void LogError(string strMsg)
        {
        ...not shown for simplicity...
        }
        
        /// <SUMMARY>

        /// Logs a warning message.

        /// </SUMMARY>

        public static void LogWarning(string strMsg)
        {
        ...not shown for simplicity...
        }

        /// <SUMMARY>

        /// Logs an information message.

        /// </SUMMARY>

        public static void LogInfo(string strMsg)
        {
        ...not shown for simplicity...
        }

        /// <SUMMARY>

        /// Logs a debug message.

        /// </SUMMARY>

        public static void LogDebug(string strMsg)
        {
#if DEBUG
        ...not shown for simplicity...
#endif //DEBUG

        }

    }
   

The following are simple test codes in AppLoggerTest project. It uses the helper class for logging.

    
    public class AppLoggerTest
    {

        public void TestDebugLog()
        {
            AppLoggerHelper.LogDebug("This is debug log.");
        }

        public void TestInfoLog()
        {
            AppLoggerHelper.LogInfo("This is info log.");
        }

        public void TestWarningLog()
        {
            AppLoggerHelper.LogWarning("This is warning log.");
        }

        public void TestErrorLog()
        {
            AppLoggerHelper.LogError("This is error log.");
        }

        public void TestExceptionLog()
        {
            try
            {
                throw new ApplicationException(
                  "This is a simulated exception.");
            }
            catch (Exception ex)
            {
                AppLoggerHelper.LogException(ex);
            }
        }   
        
        public static void Main()
        {
            AppLoggerTest objTester = new AppLoggerTest();
            objTester.TestDebugLog();
            objTester.TestInfoLog();
            objTester.TestWarningLog();
            objTester.TestErrorLog();
            objTester.TestExceptionLog();
            Console.WriteLine("Tests completed.");
         }
     }

Points of Interest

The AppLogger uses a typical Adapter pattern common to CORBA solutions by specifying interfaces (as in Definition Language - IDL). The BusinessFacade's IHome interface returns an IAppLogger interface. In my other projects, I often use a home interface to house all my other published interfaces. (In the CORBA world, there are more than one way to publish interfaces, but that's not the scope of this article.)

Instead of inventing another application framework, I have decided to use the BusinessFacade, BusinessRules, DataAccess and SystemFrameworks layers generated by Visual Studio's "Simple Distributed Application" project template. For a good treatment of the advantages of using a layered framework, you can refer to the "Best Practices" available in MSDN, or any good architecture books describing the "Microkernel" architecture pattern.

The IAppLogger interface exposes just one simple remote method, LogMessage(...), that accepts a LogMessage object as argument. Users of this method is provided an even more straight-forward API, called AppLoggerHelper class, as described above.

Clients of AppLogger need only to reference the AppLogger.BusinessFacade.dll. Any change in BusinessRules and/or DataAccess layers will have no impact to clients. This is clearly demonstrated since AppLogger allows you to change its runtime logger type to ConsoleLogger, EventLogger, DebugLogger and FileLogger. You can even implement (in the BusinessRules and DataAccess layers) a DbLogger that persists logs to database without having to change a single line of client codes.

Logging should be a short and fast action. Internal to AppLogger's BusinessRules, logging is performed by DefaultAppLogger class asynchronously using .NET's ThreadPool class. A point to note is that we can further tweak AppLoggerHelper to only log errors and exceptions during production.

In part 2 of this article, we will explore how to "refactor" AppLogger to use another asynchronous solution - MSMQ, which not only provides fast logging, but "guaranteed" delivery of messages. In the mean time, I hope you enjoy using AppLogger and keep your suggestions flowing.

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