AppLogger, a Simple Distributed Application Logger - Part 1






4.56/5 (11 votes)
Jul 7, 2004
4 min read

66263

1794
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!