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>
</appender>