Friday, 4 April 2014

Working with Log4net


  • A small introduction
  • Start working with log4net
  • Levels of logging
  • Loggers and Appenders
  • How to debug issues with log4net
  • Layouts – and types
  • Filters – and types

A Small introduction: 

From, you can see Log4net framework is based on log4j. In short we can say, log4net is for Microsoft .Net applications and log4j is for java. 
So why do we need log4net or log4j?
This tool helps the programmer to log information to different targets. 
So what kind of information we log and what are those targets?
Suppose, we have a project in production. During its daily usage, there might be some case where it might have caused some unexpected error. Now how will you track that issue? Or how will you confirm that some specific code caused that issue? This can be done by logging some information.
What is a target?
Targets can be a file system, database or any other location to log information, which we will be discussing later.

Start working with log4net:

To log some information, we need to configure it. This can be done in the following ways
BasicConfigurator: Supports logging to only one source. 
When this configurator is used, by default the logged information will be shown on Console (Supports only logging to Console). When we use this, the consoleappender is logically added to the root logger.
DomConfigurator: (deprecated. Use Xml configurator instead of this.)
XmlConfigurator: This means that we are using a file written in xml format to configure log4net (Supports logging to different places at a time).

These configurators look for a config section element with name log4net in web.config / app.config file. The below one should be added as child to the configuration element in config file:

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

This line will be added only when we are writing entire configuration in either web.config/app.config file. If we using a seperate file to configure log4net, the above lines are not necessary

Configuring using the above configurators can be done either through 
  1. Code or 
  2. Assembly-level attributes.

Using code: 

We need to write the below line well before using log4net to perform any logging operations. Generally this will be written in Application_Start(arg1,arg2) of global.asax.


We can use BasicConfigurator/ DomConfigurator in the similar way.
The static Configure() is overloaded to accept a file, stream, URI, or an XmlElement object as input to it. The parameter-less method is used to refer to the application’s web.config or app.config file.

In addition to this, we are having ConfigureAndWatch() which will accept the file info as reference.
ConfigureAndWatch (parameter) method watches the file to keep track of any changes made to it. If there is a change in the file the log4net configuration will be automatically configured.

Using Assembly level attributes:

Instead of configuring log4net via code, it can also be done using AssemblyInfo.cs file located in properties. 
We can configure log4net in the below ways:
  1. [assembly: log4net.Config.XmlConfigurator(Watch=true)]
    This, by default, looks for web.config or an app.config file to load appenders, loggers etc. This is similar to Configure() that we write from code.

  2. [assembly: log4net.Config.XmlConfigurator(ConfigFileExtension="log4net",Watch=true)]     
  3. [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4netexplicitfile.txt", Watch = true)]
    Using this, we can have a separate file for configurations to log4net instead of writing in application’s web.config or app.config file.
    Mentioning it as a separate file helps us to change the configuration without restarting the application again.
Here the parameters ConfigFile can be any file (say txt, xml etc). 
Watch: Implies, It keeps track of any changes to that file. If this is false, even if configuration file changes, there will be no impact to log4net functionality.
Suppose we have added a text file having configuration settings as below. 

<?xml version="1.0" encoding="utf-8" ?>
<appender name="AppenderFile" type="log4net.Appender.FileAppender">
<file value="LogFileName1.log" />

<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5p %d{hh:mm:ss} %message%newline" />

<level value="DEBUG" />
<appender-ref ref="AppenderFile" />

And this file has been configured using assembly statement as in point 3. 
Publish two different binaries one with watch = false and the other with watch = true.

Do the following steps with both the binaries.
  • Run the project and ensure logging is happening.
  • Open the log4netexplicitfile.txt file and change file attribute value to someother filename.
  • Perform action that logs something using log4net.
  • See whats happening with both the binaries. You should be seeing the following.

  • With watch = true: A new file is created and it will be used hereon for logging purposes.
    With watch = false: The changes done will not be recognized and will continue logging to the same old file.

Levels of logging:  

  • Debug
  • Info
  • Warn
  • Error
  • Fatal

Loggers and Appenders: 


Loggers control the levels of message that needs to be logged. It has two important parts.
  1. level
  2. appernder-ref
Consider the following logger example

<logger name="SmtpAppenderLog4net.WebForm1">
<level value="All" />
<appender-ref ref="WebForm1logger" />

Here, name of the logger is SmtpAppenderLog4net.WebForm1. This indicates, SmtpAppenderLog4net is the namespace and WebForm1 is a class inside it or inner namespace. Level mentioned is “All”. This indicates the logger accepts all the levels of logging. It can be anyone of the below:
  • All
  • Off – Logging will not be done.
  • Fatal
  • Error
  • Warn
  • Info
  • Debug

Appender-ref is which appender we should refer. Here WebForm1logger is an appender name. In the previous examples, you might have seen an xml element <appender>. Now let’s discuss what exactly an appender is.


Appenders say where to store the log messages in our application. There can be one or more appenders in the configuration. If we have multiple appenders, its name must be unique. You can see the list of appenders in For now, we will have small info on the below appenders
  • ConsoleAppender
  • FileAppender
  • Rolling file appender
  • AdoNetAppender


This appender logs messages to the console. This can be configured as below:

<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.SimpleLayout" />


This appender when used logs the messages to the specified file. This can be written as below:

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="FileToLog.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.SimpleLayout" />

Here logging of messages will be done to FileToLog file.
appendToFile element specifies whether we should append the logs to the previous content in the file or not.


This logs messages to different files depending on size or date or both. You might think we are having a fileappender and why again this RollingFileAppender. Using FileAppender is not recommended because the file can grow in size in a time frame. Opening/reading such a file takes much time. Using RollingFileAppender, we can have backup of files.

<appender name="LoggingUsingRollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="LoggingUsingRollingFileAppender.txt"/>
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="2" />
<maximumFileSize value="10MB" />
<staticLogFileName value="false" />
<countDirection value="-1"></countDirection>
<layout type="log4net.Layout.SimpleLayout" />

Here logging of messages will be done to LoggingUsingRollingFileAppender file based on size. Here I specified maximumFileSize as 10MB which means for every 1kb a new file will be created and the previous logs will be as backup.
Number of backup files that willbe saved depends on the maxSizeRollBackups value. Here at any time, 2 backup files will be present.
RollingStyle is Size. This means that rolling of files should happen based on size. Here in this example, for every 10MB file generated, rolling should happen.

We can have the following values for RollingStyle:
  • Size - Rolling will happen based on Size
  • Date - Rolling happens based on Date
  • Composite - Rolling happens considering both size and date
  • Once - Rolling happens every time log4net is configured. To check this just make some changes to the log4net configuration and run your project again. This will leave a backup file and create a new file
appendToFile element specifies whether we should append the logs to the previous content in the file or not.


Using this, we can log messages to a table in the database.
We will create a table in database and use it as source to store logs. For now I am using below SQL statement to create a table in database.

CREATE TABLE [dbo].[Log] (
[Id] [int] IDENTITY (1, 1) NOT NULL,
[message] [varchar] (2000) NULL

AdoNetAppender can be configured using the below snippet.

<appender name="SQLServerAppender" 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="Data Source=yourDataSource;Initial Catalog=YourDatabase;User ID=userid;password=password" />
<commandText value="INSERT INTO Log ( [message] ) VALUES ( @message )" />
<parameterName value="@message" />
<dbType value="String" />
<size value="2" />
<layout type="log4net.Layout.PatternLayout" value="%message" />

Element bufferSize specifies that, log4net will keep these many records in memory before inserting into the database. Suppose if we specify its value as 10, After 10 logs you will see data to be inserted into database.
dbtype is the datatype of the value.
size: here only 2 characters are allowed and logged. Suppose the log message is "Hello World", only "He" will be seen in database column.

Once I ran into an issue where I saw nothing is written to my database(I have used ADONetAppender). I crosschecked the connectionstring, the appenders part and all and felt everything is perfect. Thanks to log4net guys for adding a way to find out whats wrong with the configuration. I have added the following key to app settings in web.config(in your case may be in app.config). Ran the application again and looked into the Output window (ctrl+W,O) in Visual studio and found that I have given wrong column name.
So guys, from the next time, if you find nothing is being logged, dont forget that there is a debugging feature in log4net.

Layouts and types:

In appender section, you might have seen xml element <layout> . So what is a layout? Layout is just a template for your messages. You can customize your message using this.
  • SimpleLayout
  • PatternLayout
  • XMLLayout
  • RawPropertyLayout
  • RawTimeStampLayout
  • RawUtcTimeStampLayout etc…...


This is the simplest layout which contains minimal information about the logged message. It gives only level and the message of the logged message

<appender name="WebForm1logger" type="log4net.Appender.FileAppender">
<file value="WebForm1logger.txt" />
<layout type="log4net.Layout.SimpleLayout"></layout>

Logs in WebForm1logger.txt will be of format level_of_logged_message message


This is similar to printf statement in C or the string.Format() in C#. Simply to say, it is a conversion pattern. We have the flexibility to format the message by adding some information to it.

Some of the conversion patterns are listed below:

Pattern name Effect seen at the time of logging event
Appdomain Outputs appdomain name
Date Outputs date
Exception Outputs exception passed with the message
Level Outputs level of logging event
Logger Outputs the logger that logged the event
Message Outputs the message passed with the logging event call
Newline Inserts a new line
Property Used to output an event specific property. The property will be specified within braces. Example : property{customproperty}. This will look for the key named customproperty and will get the value associated to it. If no specific key is given, then all the keys and their values are printed in a comma separated list.
Timestamp Used to output the number of milliseconds elapsed since start of the application till the logging event.
Thread Used to output name of the thread. If no name is given, number will be considered.
Utcdate Outputs date in universal time.
% Prints a %

We can use PatternLayout using the below code:

<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date - %message%newline" />

This results in date - message_passed_from_the_logger


This logs information in xml format.This can be used in the following way:

<layout type="log4net.Layout.XMLLayout"></layout>


This is mostly used in ADOAppender while adding value to new column. This can be used as:

<parameterName value="@Email" />
<dbType value="String"/>
<size value="255"/>
<layout type="log4net.Layout.RawPropertyLayout">
<key value="CustomPropertyEmailid"></key>

Here Email is the column name in log4net. We are binding a property “CustomPropertyEmailid” value to this column. Value to this property can be set using the following line (this is one of the way):

log4net.ThreadContext.Properties["CustomPropertyEmailid"] = "";

RawTimeStampLayout, RawUtcTimeStampLayout will be used mostly in ADONetAppender.

Filters - Its types

Suppose there is a case where you need to log Info, debug, warn level logs to one file and Error, Fatal level logs to other. Or suppose we need to log messages that match certain search criteria or reject messages matching some criteria. How this can be accomplished? Log4net comes up with Filters concept to accomplish this. It has the following filters.
  • LevelMatchFilter
  • LevelRangeFilter
  • LoggerMatchFilter
  • StringMatchFilter
  • PropertyFilter
  • DenyAllFilter
LevelMatchFilter: This filters messages against this specific level. It accepts the following properties
levelToMatch: this contains the level that needs to be matched.
acceptOnMatch: Boolean value to indicate whether to accept this level or reject it. Default is true

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="Debuglogs.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %logger %message %newline "/>
<filter type="log4net.Filter.LevelMatchFilter">
<acceptOnMatch value="true" />
<levelToMatch value="DEBUG" />
<filter type="log4net.Filter.DenyAllFilter" />

The above filter logs only when the level matches debug. You can check this in Debuglogs file. Dont forget to include denyAllFilter filter.

LevelRangeFilter: This filters messages that fall within the range specified or not in the range depending on acceptToMatch property.

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="DebugToWarnlogs.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %logger %message %newline "/>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG" />
<levelMax value="WARN" />
<filter type="log4net.Filter.DenyAllFilter" />

The above filter logs only when the level is in range of debug to warn. All others will be ignored. You can check this in DebugToWarnlogs file. Dont forget to include denyAllFilter filter.

LoggerMatchFilter: This filters messages against the logger that logged the message. This uses startsWith() operation to check for logger match.

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="loggerMatchFilter.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %logger %message %newline "/>
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="MyLog4netProject"></loggerToMatch>
<filter type="log4net.Filter.DenyAllFilter" />

Here the logger that will be match will be those that start with "MyLog4netProject".

StringMatchFilter: This helps to filter messages based on the message content. This can be configured as below:
regexToMatch: regex expression can be given to match the log message.
StringToMatch: Filters messages by checking whether message contains this string or not. It is case sensitive search.
acceptOnMatch: Boolean property.

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="StringMatchFilter.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %logger %message %newline "/>
<filter type="log4net.Filter.StringMatchFilter">
<stringToMatch value="MyLog4netProject"></stringToMatch>
<filter type="log4net.Filter.DenyAllFilter" />

The above example logs messages if the message contains the string "MyLog4netProject". The search is case sensitive.
Usually, we specify either regexToMatch or stringToMatch property. If we specify both, regexToMatch will be considered and stringToMatch will be simply ignored.

PropertyFilter: Here match is made against the value of the property specified in the “key” attribute.
Key: Here we will specify on which property the match should happen.
And all the attributes available for StringMatchFilter are also available for PropertyFilter.

DenyAllFilter: This denies logging. Usually we will specify this at the end of all filters. This will be added as below.

<filter type="log4net.Filter.DenyAllFilter" />

Any suggestions are welcomed. Please comment if anyone has any issue with this post.

No comments:

Post a Comment