[ZT]4 Tips for using Log4NET's ADONetAppender in ASP.NET
I'm using Log4NET for almost a year now, and I must say I'm very pleased with it. This logging framework is very easy to learn and extend; same goes for installation and configuration.
The most commonly used log4net appender is probably the ADONetAppender (look at Ryan's post for an easy walkthrough to set this up). However, I spent a couple of hours getting the ADONetAppender working the way I want. If you're also using the ADONetAppender, these tips can be helpful.
Tip 1: Configure log4net on Application_Start
After adding the log4net configSection and appenders in your web.config, you need to run this line of code in the Application_Start of your Global.asax:
log4net.Config.DOMConfigurator.Configure();
I've no clue why log4net needs this extra kickstart here, because I think the Log4NetConfigurationSectionHandler should be able do this configuration itself.
Tip 2: In the ADONetAppender settings (inside web.config), set the bufferSize to 1.
A lot of examples show a bufferSize of 100, which means nothing is written to the Log table until 100 messages are logged. This is probably the reason why most developers think ADONetAppender doesn't work properly.
Tip 3: Change the settings of an appender at runtime.
The ADONetAppender has a connectionString setting, which contains the connectionstring to the database where the Log table is located. However, most WebApplications already have a connectionstring defined somewhere in the web.config (for instance in the appSettings), and you don't want the connectionstring defined twice or more. Assume my logger is called 'MyProject', and contains a reference to the ADONetAppender:
<logger name="MyProject">
<appender-ref ref="ADONetAppender" />
</logger>
Now you can add this piece of code in the Application_Start (inside global.asax) to change the connectionString setting of the ADONetAppender at runtime to the value of the "MyConnectionString" appSetting in the web.config:
// Get the Hierarchy object that organizes the loggers
log4net.Repository.Hierarchy.Hierarchy hier =
log4net.LogManager.GetLoggerRepository() as log4net.Repository.Hierarchy.Hierarchy;if (hier != null)
{
//get ADONetAppender
log4net.Appender.ADONetAppender adoAppender =
(log4net.Appender.ADONetAppender)hier.GetLogger("MyProject",
hier.LoggerFactory).GetAppender("ADONetAppender");
if (adoAppender != null)
{
adoAppender.ConnectionString =
System.Configuration.ConfigurationSettings.AppSettings["MyConnectionString"];
adoAppender.ActivateOptions(); //refresh settings of appender
}
}
Tip 4: Use log4net's MDC (Mapped Diagnostic Context) to add extra information to your Log table.
The ADONetAppender is very easy to extend. If want to add the name of the current user to the Log table, you can do this in three steps:
1. Add a new column 'User' to the Log table
2. When logging a message, use MDC.Set("user", ...) to add the user name to the log4net context
3. Change the commandText of the ADONetAppender in the web.config and add a parameter to
hold the user name (using the %X{user} conversion pattern)
The log4net MDC.Set() code:
public void LogError (string message, Exception e)
{
//get logger
ILog logger = LogManager.GetLogger("MyProject");//set user to log4net context, so we can use %X{user} in the appenders
if (HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated)
MDC.Set("user", HttpContext.Current.User.Identity.Name);if (logger.IsErrorEnabled)
logger.Error(message, e); //now log error
}
The appender in web.config:
<!--
CREATE TABLE [dbo].[Log] (
[Id] [int] IDENTITY (1, 1) NOT NULL,
[Date] [datetime] NOT NULL,
[Thread] [varchar] (255) NOT NULL,
[Level] [varchar] (50) NOT NULL,
[Logger] [varchar] (255) NOT NULL,
[User] [varchar] (50) NULL,
[Message] [varchar] (4000) NOT NULL,
[Exception] [varchar] (2000) NULL
)
-->
<appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data,
Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="[we will set this automatically at runtime]" />
<commandText value="INSERT INTO Log ([Date],[Level],[Logger],[User],[Message],[Exception])
VALUES (@log_date, @log_level, @logger, @user, @message, @exception)" />
<parameter>
<parameterName value="@log_date" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="@log_level" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%p" />
</layout>
</parameter>
<parameter>
<parameterName value="@logger" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%c" />
</layout>
</parameter>
<parameterName value="@user" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%X{user}" />
</layout>
</parameter>
<parameter>
<parameterName value="@message" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%m" />
</layout>
</parameter>
<parameter>
<parameterName value="@exception" />
<dbType value="String" />
<size value="2000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>