Monday, July 19, 2010

Log4Net Demo

When it comes to logging, there is one open source library that stands out, Log4Net.  One could even go as far as saying its the defacto standard for .Net logging.  At the very least you would be wasting a great deal of time reproducing even the basic functionality this library provides...for free.  Did I mention its free?  

The best way to demo this is with a code walk through.  I have created a C# Console application and have this code in the Program.cs file:


namespace Log4NetDemo {
    using log4net;
    using log4net.Config;

    public class LogTest {
        private static readonly ILog logger = LogManager.GetLogger(typeof(LogTest));

        static void Main(string[] args) {
            XmlConfigurator.Configure();  // Directs Log4Net to use the app.config

            logger.Debug("Here is a debug log.");
            logger.Info("... and an Info log.");
            logger.Warn("... and a warning.");
            logger.Error("... and an error.");
            logger.Fatal("... and a fatal error.");
        }
    }
}

The DOMConfigurator.Configure(); line is used to point Log4Net to the app.config file to read in its configuration.  You could implement a trigger in your application to re-read the settings if required.
Log4Net provides ultimate flexibility by reading log "appenders" from the app.config file.  In fact all its configuration comes from the app.config file.



<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
    </configSections>

    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
    </startup>

    <!-- Logging config -->
    <log4net>
        <!-- Debug Log Appender -->
        <appender name="OutputDebugStringAppender" type="log4net.Appender.OutputDebugStringAppender">
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%5.5thread%6.6level %35.35logger:[%4.4thread] %message"/>
            </layout>
        </appender>

        <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
            <file value=".\RollingLog.log"/>
            <appendToFile value="true"/>
            <maximumFileSize value="1024KB"/>
            <maxSizeRollBackups value="2"/>
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%5.5thread%6.6level %35.35logger:[%4.4thread] %message"/>
            </layout>
        </appender>
        
        <appender name="AspNetTraceAppender" type="log4net.Appender.AspNetTraceAppender">
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/>
            </layout>
        </appender>
        
        <appender name="FileAppender" type="log4net.Appender.FileAppender">
            <file value=".\AppenderLog.log"/>
            <appendToFile value="true"/>
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/>
            </layout>
        </appender>

        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
            <layout type="log4net.Layout.PatternLayout">
                <param name="Header" value="[Header]\r\n" />
                <param name="Footer" value="[Footer]\r\n" />
                <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n" />
            </layout>
        </appender>

        <!-- Set up root logger -->
        <root>
            <!-- levels: DEBUG, INFO, WARN, ERROR, FATAL, OFF    -->
            <level value="ALL"/>
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="FileAppender"/>
            <appender-ref ref="ConsoleAppender"/>
            <appender-ref ref="OutputDebugStringAppender"/>
        </root>
    </log4net>
</configuration>

Things to note here are the appender declarations.  You can have as many or as few of these as you like, and obviously these can be changed at runtime.  Each appender has its own configuration element defining where and what format to log.
Note how the AspNetTraceAppender is declared but not referenced by the root element.  This means it will not be logged to at runtime. It might just be there in case someone would like to use it in the future.  This practise saves looking up the declaration syntax when you in hurry to activate the logging.

For a complete list of appenders see the Log4Net site.  At last count there were approaching 40 built-in appenders.

Here's the output of the console app:


2010-06-19 10:30:45,613 [6964] DEBUG Log4NetDemo.LogTest Here is a debug log.
2010-06-19 10:30:45,639 [6964] INFO  Log4NetDemo.LogTest ... and an Info log.
2010-06-19 10:30:45,639 [6964] WARN  Log4NetDemo.LogTest ... and a warning.
2010-06-19 10:30:45,639 [6964] ERROR Log4NetDemo.LogTest ... and an error.
2010-06-19 10:30:45,639 [6964] FATAL Log4NetDemo.LogTest ... and a fatal error.
Press any key to continue . . .


In The console window you can see the output of the ConsoleAppender.  The other 3 appenders defined in the app.config are logging elsewhere.
Two files have been created in the same folder as the console app's exe:


2010-06-19 10:16:26,416 [7184] DEBUG Log4NetDemo.LogTest [(null)] - Here is a debug log.
2010-06-19 10:16:26,444 [7184] INFO  Log4NetDemo.LogTest [(null)] - ... and an Info log.
2010-06-19 10:16:26,444 [7184] WARN  Log4NetDemo.LogTest [(null)] - ... and a warning.
2010-06-19 10:16:26,444 [7184] ERROR Log4NetDemo.LogTest [(null)] - ... and an error.
2010-06-19 10:16:26,444 [7184] FATAL Log4NetDemo.LogTest [(null)] - ... and a fatal error.
2010-06-19 10:30:45,613 [6964] DEBUG Log4NetDemo.LogTest [(null)] - Here is a debug log.
2010-06-19 10:30:45,639 [6964] INFO  Log4NetDemo.LogTest [(null)] - ... and an Info log.
2010-06-19 10:30:45,639 [6964] WARN  Log4NetDemo.LogTest [(null)] - ... and a warning.
2010-06-19 10:30:45,639 [6964] ERROR Log4NetDemo.LogTest [(null)] - ... and an error.
2010-06-19 10:30:45,639 [6964] FATAL Log4NetDemo.LogTest [(null)] - ... and a fatal error.

AppenderLog.log


 7184 DEBUG                 Log4NetDemo.LogTest:[7184] Here is a debug log. 7184  INFO                 Log4NetDemo.LogTest:[7184] ... and an Info log. 7184  WARN                 Log4NetDemo.LogTest:[7184] ... and a warning. 7184 ERROR                 Log4NetDemo.LogTest:[7184] ... and an error. 7184 FATAL                 Log4NetDemo.LogTest:[7184] ... and a fatal error. 6964 DEBUG                 Log4NetDemo.LogTest:[6964] Here is a debug log. 6964  INFO                 Log4NetDemo.LogTest:[6964] ... and an Info log. 6964  WARN                 Log4NetDemo.LogTest:[6964] ... and a warning. 6964 ERROR                 Log4NetDemo.LogTest:[6964] ... and an error. 6964 FATAL                 Log4NetDemo.LogTest:[6964] ... and a fatal error.
RollingLog.log
Note how this log file does not contain any new-lines. This is because the format in the app.config did not include the %newline glyph.

<conversionPattern value="%5.5thread%6.6level %35.35logger:[%4.4thread] %message"/>
Compare this to the AppenderLog.log format:

<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/>
And last but not least the logging information was also being logged to the Debug window in Vs.  DebugView is another useful utility to pick up text sent to the debug stream.  Here's the logging output as picked up by DebugView.


Extensibility of Log4Net
Firstly if you're like me you probably have an interface to isolate your logger implementation from you consuming code.  Obviously you can implement a new derived type that directs logging to log4net.  So choosing to use Log4Net should be an issue to start using within an existing app.

Log4Net provides extensive support for custom formats, filtering and different style of using the appenders. See thisLog4Net page for more information.  In my use of the library I have never had to write a custom plug-in for Log4Net but that is possible too. See the Plug-In page for more information.


Hope that helps someone.
B.

No comments:

Post a Comment