Wednesday, May 21, 2008

log4net Configuration made simple through Attributes

I'm sure this is well documented, but for my own reference and your convenience, here's one from my list of favorite log4net tips and tricks: how to instrument your code so that log4net automatically picks up your configuration.

On average, I've been so happy with how well log4net has fit my application logging needs that most of my projects end up using it: console apps, web applications, class libraries. Needless to say I use it a lot, and I get tired of writing the same configuration code over and over:

private static void Main()
{
    string basePath = AppDomain.CurrentDomain.BaseDirectory;
    string filePath = Path.Combine(basePath, "FileName.log4net");
    XmlConfigurator.ConfigureAndWatch(new FileInfo(filePath));
}

log4net documentation refers to a Configuration Attribute (XmlConfiguratorAttribute), but it can be frustrating to use if you're not sure how to set it up. The trick is how you name your configuration file and where you put it. I'll walk through how I set it up...

log4net using XmlConfiguratorAttribute Walkthrough

  1. Add an Assembly Configuration Attribute: log4net will look for this configuration attribute the first time you make a call to a logger. I typically give my configuration file a "log4net" extension. Place the following configuration attribute in the AssemblyInfo.cs file in the assembly that contains the main entry point for the application.

    [assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "log4net",Watch = true)]

  2. Create your configuration file: As mentioned previously, the name of the configuration file is important as is where you put it. In general, the name of the configuration file should follow the convention: full-assembly-name.extension.log4net. The file needs to be at the base folder of the application, so for WinForms and Console applications it resides in the same folder as the main executable, for ASP.NET applications it's the root of the web-site along side the web.config file.

    Project Type Project Output log4net file name Location
    WinForm App Program.exe Program.exe.log4net with exe
    Console App Console.exe Console.exe.log4net with exe
    Class Library Library.dll N/A  
    ASP.NET /bin/Web.dll /Web.dll.log4net Web root (/)

  3. Define your Configuration Settings: Copy and paste the following sample into a new file. I'm using the Rolling Appender as this creates a new log file every time the app is restarted.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>

    <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
    </configSections>

    <log4net>
    <!-- Define output appenders -->
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="log.txt" />
    <appendToFile value="true" />
    <rollingStyle value="Once" /> <!-- new log file on restart -->
    <maxSizeRollBackups value="10"/> <!-- renames rolled files on startup 1-10, no more than 10 -->
    <datePattern value="yyyyMMdd" />
    <layout type="log4net.Layout.PatternLayout">
    <param name="Header" value="[START LOG]&#13;&#10;" />
    <param name="Footer" value="[END LOG]&#13;&#10;" />
    <conversionPattern value="%d [%t] %-5p %c [%x] - %m%n" />
    </layout>
    </appender>

    <!-- Setup the root category, add the appenders and set the default level -->
    <root>
    <level value="DEBUG" />
    <appender-ref ref="RollingLogFileAppender" />
    </root>

    </log4net>
    </configuration>
  4. Make a logging call as early as possible: In order for the configuration attribute to be invoked, you need to make a logging call in the assembly that contains that attribute. Note I declare the logger as static readonly as a JIT optimization.

    namespace example
    {
    public class Global : System.Web.HttpApplication
    {
    private static readonly ILog log = LogManager.GetLogger(typeof(Global));

    protected void Application_Start(object sender, EventArgs e)
    {
    log.Info("Web Application Start.");
    }
    }
    }

Cheers.

submit to reddit

6 comments:

shlomik said...

if you use log4net so often, I was wondering with witch viewer do you use?

bryan said...

Hmmm, good question. Oddly enough, now that I think of it, I can't claim that I have a favorite tool for log4net! I think that's largely because of two reasons:

1. the framework is so extensible that I tailor my configuration to suit my project's needs. I've used smtp, udp, database, console, trace, event logs and rolling file appenders in some shape or form, either alone or in combination with one another. The type of project will also dictate logging requirements, ie console app versus Windows Service versus 20 load balanced web-servers...

2. my logging needs change at different stages of the project. During development, I pipe debug information to files and console windows mainly to see that the information is getting logged as expected (correct values, order, occurrence); During QA emphasis shifts to make sure logs contain meaningful data when errors occur; in Production, I want to be notified when problems occur via email, snmp, rss, etc.

Personally, I use Notepad++ for the majority of simple log file viewing.

The main challenge with file-based readers is how they deal with content that has line-breaks (Stack Traces, rtf, xml nodes, etc) -- this type of content often is interpreted as several rows instead of a single record. If you solve this problem, then any reader will do: Excel, LogParser, etc. Stay tuned, sounds like another blog post.

shlomik said...

did I spell 'witch' ?? :-)

Anyway, I'm using log4net quite extensively as well. In my current project, I have services oriented architecture and to monitor them I use the udp appender to one centralized viewer.
I tried at first 'Chainsaw', but I really didn't like the execution of it (it's java and requires to have an annoying console window, moreover the configuration of it wasn't so nice. Lately I found 'Log4View' and currently it works smoothly and suits my needs. The only problem that it's not free. It's kinda weird that the log4net library quite widely used, and there's no free worthy viewer!

anyway, thanks for your help, I'm gonna keep an eye on your posts :-)

bryan said...

Just an update...

I've got a post that demonstrates how to create excel friendly log4net output: Producing readable log4net output

Christian said...

@shlomik
you can use my unix-like tail implementation.
1. it's free
2. it's easy to install (click-once)

get it here whenever you need it:

http://www.connexin.ch/cxntail

cheers
Christian

Niels Brinch said...

Thanks.

If you can spare the performance, nothing really beats logging to the database, so you get a source that's easily searchable.

In case you some times forget to put the right class name when setting up log4net for a specific class, you can use this declaration instead.

private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);