Dionoid

ALL WEB MUST SCALE!
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>

Published Wednesday, February 16, 2005 11:13 PM by Dr.NETjes

Filed under: ,

Comments

# re: 4 Tips for using Log4NET's ADONetAppender in ASP.NET@ Monday, April 27, 2009 9:12 AM

Thanks for your post...

But i cannot agree with you on setting buffer size to 1, For immediatte log info is better use file appender and ado appender let with buffer 100 ... Consider performance penalty ...

miro

# re: 4 Tips for using Log4NET's ADONetAppender in ASP.NET@ Thursday, May 7, 2009 8:53 PM

In log4net 1.2.10 the MDC (Mapped Diagnostic Context) has been replaced with thread context properties.

See logging.apache.org/.../contexts.html

Daniel Ballinger

# re: 4 Tips for using Log4NET's ADONetAppender in ASP.NET@ Friday, June 19, 2009 6:55 PM

Regarding the user implementation with ThreadContext...

cs code:

log4net.ThreadContext.Properties["User"] = "username";

web.config:

<parameterName value="@user" />

 <dbType value="String" />

 <size value="50" />

 <layout type="log4net.Layout.PatternLayout">

  <conversionPattern value="%property{Log_User}" />

 </layout>

</parameter>

ProudGeekDad

# re: 4 Tips for using Log4NET's ADONetAppender in ASP.NET@ Thursday, September 29, 2011 8:35 PM

Could you help me determine why I am getting a null adoApender when I execute this code.  What is "MyProject" representing, the name of the project or the name of the solution or the name of something else?

//get ADONetAppender  log4net.Appender.ADONetAppender adoAppender =     (log4net.Appender.ADONetAppender)hier.GetLogger("MyProject",      hier.LoggerFactory).GetAppender("ADONetAppender");

Daniel

# re: 4 Tips for using Log4NET's ADONetAppender in ASP.NET@ Wednesday, February 22, 2012 11:36 AM

You are a genius. I installed log4net with NuGet and expected it to work. It didn't. Tips 1 and 2 worked brilliantly.

Ashiq

# re: 4 Tips for using Log4NET's ADONetAppender in ASP.NET@ Thursday, September 13, 2012 11:19 AM

You should also add the tip to use:

<reconnectOnError Value="true"/>

With the AdoNetAppender.

David Grenier

# re: 4 Tips for using Log4NET's ADONetAppender in ASP.NET@ Thursday, February 14, 2013 1:27 AM

Thanks a lot.

I have a problem with not executing query right after print log.

buffer size is the main point !! Thanks !!

Kim Jin Hee