Keeping A Log
The Exception Management Application Block is useful for writing out information about exceptions; however, in services it is often necessary to do quite a bit more logging and very useful to have different levels of logging.
Coming from a Java background, I really enjoyed the log4j framework available from the Apache Web site. The log4net project offers almost all of the same features of the log4j framework, except in an assembly that can be used in a .NET project.
Of those features, one of the most useful I found when logging in services is the ability to set logging levels on a per-class basis. Another very useful feature is the ability to very easily create a rolling-log logging system, where logs are "rolled over" when the reach a configured size. The number of log files that are retained is also configurable, making this is an easy way to log quite a bit of information out to files without being concerned about filling up the disk.
You can download log4net from the Apache Web site. Like using the Exception Management Application Block (or any other third-party library, for that matter), you will have to compile and install the library in a manner that is consistent with the rest of your projects. Once you have added the appropriate reference to the log4net assembly, add the line:
at the top of the class that uses the log4net framework. After adding the references and using statement, there are three tasks that need to be done: add the configuration for log4net in the application's configuration file, declare an instance of
log4net.ILog, and use the Configure method to set up and configure the log4net logger.
In these examples, I'll assume that the configuration for log4net is contained in the
application's configuration file. It doesn't have to be located there; the
Configure method allows the name of the file to be passed in as an argument.
The logger configuration section will look like this in the
<logger name="AuthenticationPlugins.UI.UITester.Form1"> <level value="INFO"/> <appender-ref ref="RollingFileAppender" /> </logger>
The logger uses an appender to know where to log the messages. A
ConsoleAppender for instance, will print the message out to the console, provided the log message is in the correct level. The level sets the minimum severity that will be printed to the appender: DEBUG, INFO, WARN, ERROR, or FATAL (listed in order from least severe to most severe). If the level is set to INFO, as it is above, any message higher than INFO will be sent to the appender.
The appender shown in Listing 1 is the
RollingFileAppender, which has its own configuration section as shown in Listing 2.
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="C:\\Temp\\test.log" /> <appendToFile value="true"/> <rollingStyle value="Size"/> <maxSizeRollBackups value="10" /> <maximumFileSize value="1000KB" /> <staticLogFileName value="true" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="[%d] %-5p : %m%n" /> </layout> </appender>
This particular configuration sets the RollingFileAppender to write to a file called
C:\Temp\test.log. Once the file reaches 1000KB, the appender rolls the file over to
C:\Temp\test.log.1. It will keep rolling the files over until there are 10 of them, set by
maxSizeRollBackups above. I won't go into the
conversionPattern here, but just mention that the configuration above prints a line that is similar to the one show here in the log file:
[2004-06-01 10:00:00,343] INFO : Loading plug-ins.
appender sections are nested inside the
log4net configuration section:
<log4net> <logger> ... </logger> <appender> ... </appender> </log4net>
After building the
log4net configuration section and adding it to the configuration file, you must add a handler for the section (for more information on why, see Part 2):
<configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"> </configSections>
Now that the application's configuration section is added and a handler is set up to process it, I'll move on to the remaining two tasks for setting up log4net: declaring an object that uses the log4net.ILog interface, and setting up the object in the application.
To declare and create an instance of the logger, type:
private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger( typeof(Form1) );
just under the
ArrayList plugins declaration line for
Back in Listing 1, I named the logger the fully-qualified class name of
I can really name it anything, like "moo", and retrieve it with that name, but I've found that naming it the class name makes referencing the logger in the configuration file much easier.
The last thing to do before you can be off and using log4net to merrily log messages is to initialize it in the code by adding:
log4net.Config.DOMConfigurator.Configure( new System.IO.FileInfo( AppDomain.CurrentDomain.SetupInformation.ConfigurationFile) );
in a method that will automatically be called when the application is loaded, like the
Form1 constructor. Once the
Configure method is called, you can call the methods of the
ILog interface on
Logger to log messages:
Logger.Info( "Starting UITester." );
ILog interface has methods that correspond with the log levels. Debug is used for DEBUG level messages, Info logs INFO messages, and so on.