# Sunday, November 21, 2004
« Making a page expire | Main | My trek into automated builds »

If you are looking to add logging to your application, whether for error logging or debugging or tracing, one decent option is the open source log4net. I give it a “decent” only in that there seems to be some bugs, and I am still not totally sold on the open source concept for a variety of reasons. But given that, some good thought has been placed into log4net, so that you have a good variety of options on where and in what format your logging information will be output. Note that I looked at v2, since it's the one which allows you to output to the database.

I am a huge proponent of logging error information to a database because you can check on errors real-time, as well as give yourself some powerful searching mechanisms. The client I was working for was logging to a file using an older version of log4net, so I decided to check out the log4net database functionality. Here's where you can find all the information you may want to know, including how to download log4net: http://logging.apache.org/log4net/

What I found was mixed. On the upside, you can (to a degree) use log4net with an existing database error log to a certain degree (I say to a certain degree because without going in an changing the source -- which is a plus for open source). A lot of thought has been placed into the design with regards to flexibility and performance.

On the downside, the code seemed buggy and the documentation is pretty bad. In order to get v2 database logging to work, I had to resort to stepping into the code to determine what the correct configuration settings would be. So as a bonus today, I will save the reader any heartache and post my file, along with a description of what the reader will need to change to use it!

First, I created a database (called test) in SQL Server 2000, and created a table called Log. Here's the fields, and I will leave the size out so you can make your own decisions about that, but given the amount of detail in the Message parameter of an error message, you might want to make some big varchars in there.

Column Type
ErrorId Identity (int)
Date datetime
Thread varchar
Level int
Logger varchar
Message varchar
Exception varchar

Create the table, then configure the log4net configuration file. Here's what mine looked like with the table structure above:

<?xml version="1.0" encoding="utf-8" ?>
<log4net debug="false">
<appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
<param name="BufferSize" value="1" />
<param name="ConnectionType" value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<param name="ConnectionString" value="server=localhost;uid=test;pwd=test;database=test" />
<param name="CommandText" value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" />
<param name="Parameter">
<param name="ParameterName"&#0;value="@log_date" />
<param name="DbType" value="DateTime" />
<param name="Layout" type="log4net.Layout.RawTimeStampLayout" />
</param>
<param name="Parameter">
<param name="ParameterName"&#0;value="@thread" />
<param name="DbType" value="String" />
<param name="Size" value="255" />
<param name="Layout" type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%t" />
</param>
</param>
<param name="Parameter">
<param name="ParameterName"&#0;value="@log_level" />
<param name="DbType" value="String" />
<param name="Size" value="50" />
<param name="Layout" type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%p" />
</param>
</param>
<param name="Parameter">
<param name="ParameterName"&#0;value="@logger" />
<param name="DbType" value="String" />
<param name="Size" value="255" />
<param name="Layout" type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%c" />
</param>
</param>
<param name="Parameter">
<param name="ParameterName"&#0;value="@message" />
<param name="DbType" value="String" />
<param name="Size" value="4000" />
<param name="Layout" type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%m" />
</param>
</param>
<param name="Parameter">
<param name="ParameterName"&#0;value="@exception" />
<param name="DbType" value="String" />
<param name="Size" value="2000" />
<param name="Layout" type="log4net.Layout.ExceptionLayout" />
</param>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="ADONetAppender" /
</root>
</log4net>

config.log4net (2.32 KB)

Of course, first thing you'll want to do is change the ConnectionString value to your situation. Now, some weird things I found. It doesn't seem to use the stored procedure it says it does. I stepped through the source, and it looks like it just tries to execute a SQL statement, which can be found in the CommandText parameter in the configuration file. So really, you could likely strip out all the parameters listed above. I have to confess it has been some months since I looked at log4net, so there might be a reason I left them in, such as a different part of the code that uses them and without it, it fails. So, take a few minutes and strip them out, and see what you get. But the saving you get from my slogging through the code should more than justify looking at this configuration file.

My final review is this: If you're creating a production app, here's your two options: roll your own solution or use log4net. If you plan on using log4net, download the source, learn it, and be prepared to spend some time (possibly considerable) debugging it. As a consultant, unless you need to ability to switch logging types (e.g. database to file to application log) dynamically, I would recommend rolling your own logging system, and write it such you can use it in multiple applications. Use log4net to get some ideas on the best way to accomplish it. Why? I think it tries to be too much to everyone, and it's not quite yet complete, and the documentation is lacking. In the end, KISS wins (keep it simple, stupid).