Monday, July 3, 2017

log4net tutorial– Great library for logging

A lot of applications use logging as a way to store information about performed operations. Such data is very useful when you have to diagnose an issue. Logging could be done on many ways, but sometimes it’s just easier to use an existing solution. I’d like to introduce one of many libraries for logging, but quite powerful – log4net.

Get log4net

Let’s create a console application – log4netTutorial. Next you have to reference log4net library. The easiest way is to install NuGet package:
  • Right click your project
  • Click Manage NuGet Packages…
  • Click Online on the left hand panel
  • Type log4net in search box on the right side
  • Select log4net package and click Install
log4net NuGet pkg

How to use log4net

Now, let’s open Program.cs and add following field.
private static readonly log4net.ILog log =
    log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
The filed is initialized with log4net logger witch takes the type in which the filed is declared – this way it will be quite easy to identify what logged a message. So let’s try to log something.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace log4netTutorial
{
    class Program
    {
        private static readonly log4net.ILog log =
            log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        static void Main(string[] args)
        {
            log.Info("Application is working");
        }
    }
}
Run the program and… see nothing. Don’t worry, that’s expected.

Basic log4net configuration

You need to let log4net know where to put the data. First open AssemblyInfo.cs
AssemblyInfo
Add following line.
[assembly: log4net.Config.XmlConfigurator(Watch=true)]
This way log4net will know that it can look for configuration in the default application config file. Next, let’s open the config (App.config) and add following.
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
This time log4net will know that the real configuration is stored in XML node called log4net. So let’s add it.

<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  <log4net>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

log4net message levels

Now, let’s think a bit how log4net works. There are 5 log message levels:
  • FATAL
  • ERROR
  • WARN
  • INFO
  • DEBUG
In the Program.cs file we used log.Info  method which logs INFO level message. You can use different method if you want to log different level messages.
There are also logging levels:
  1. OFF – no logging
  2. FATAL
  3. ERROR
  4. WARN
  5. INFO
  6. DEBUG
  7. ALL – everything is logged
You can specify what is the highest logging level in the config. For example, if you specify:
  • OFF – no message will be logged
  • FATAL – only FATAL messages will be logged
  • WARN – FATAL, ERROR and WARN messages will be logged
  • ALL – every message will be logged
To specify the logging level you have to add root node, then inside level node with value attribute.

<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  <log4net>
    <root>
      <level value="ALL" />
    </root>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

log4net appenders

Console appender

Finally, you have to define where to put messages. In log4net there is a concept of appenders to which messages are appended. In the rootnode you can add appender-ref node with ref attribute which contains the name of appender to use. For example MyAppender.

<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="MyAppender" />
    </root>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>
Next, let’s define the appender. The simplest one is ConsoleAppender which logs messages to the application console.

<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="MyAppender" />
    </root>
    <appender name="MyAppender" type="log4net.Appender.ConsoleAppender">
    </appender>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>
Inside the appender XML node you have to define the message format. There is PatternLayout type which is commonly used.

<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="MyAppender" />
    </root>
    <appender name="MyAppender" type="log4net.Appender.ConsoleAppender">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %level %logger - %message%newline" />
      </layout>
    </appender>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>
When you run the application you will see similar output:
2015-06-12 20:50:13,256 INFO log4netTutorial.Program - Application works
Press any key to continue . . .
The %name elements in the pattern are special tags that are replaced with values. Here is a short list of the most useful:
  • %date – date in local time zone
  • %utcdate – the same as %date, but in universal time
  • %level – message level
  • %logger – the logger declaring type (passed to GetLogger method; example in first C# code from the top of this post)
  • %message – the message
  • %newline – new line entry based on the platform on which it’s used

File appender

You can also define an appender that will add messages to a file.
    <appender name="MyFileAppender" type="log4net.Appender.FileAppender">
      <file value="application.log" />
      <appendToFile value="true" />
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %level %logger - %message%newline" />
      </layout>
    </appender>
More, you can use few appenders at the same time.

<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="MyAppender" />
      <appender-ref ref="MyFileAppender" />
    </root>
    <appender name="MyAppender" type="log4net.Appender.ConsoleAppender">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %level %logger - %message%newline" />
      </layout>
    </appender>
    <appender name="MyFileAppender" type="log4net.Appender.FileAppender">
      <file value="application.log" />
      <appendToFile value="true" />
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %level %logger - %message%newline" />
      </layout>
    </appender>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

Rolling file appender

There is one last very useful appender that I can recommend. Let’s assume that your application has to log a lot of messages, maybe 10’s of MB a day, maybe more. After few weeks of using your application might consume few GBs. This might be a case that you don’t want to have. Maybe it would be better if you could keep only a piece of that – the most recent one? log4net can help with that as well – there is a concept of rolling log. Rolling file has a maximum size defined. When the size is reached the file is backed up and new one is created. There is also defined a number of backups to keep.

<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="MyAppender" />
      <appender-ref ref="RollingFileAppender" />
    </root>
    <appender name="MyAppender" type="log4net.Appender.ConsoleAppender">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %level %logger - %message%newline" />
      </layout>
    </appender>
    <appender name="MyFileAppender" type="log4net.Appender.FileAppender">
      <file value="application.log" />
      <appendToFile value="true" />
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %level %logger - %message%newline" />
      </layout>
    </appender>
    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
      <file value="rolling.log" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <maxSizeRollBackups value="5" />
      <maximumFileSize value="10MB" />
      <staticLogFileName value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
      </layout>
    </appender>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

log4net summary

This is just overview of log4net capabilities. I just wanted to present a quick start and basic configuration. If you need more details there is a good log4net tutorial on codeproject. Here is also log4net manual available.

No comments: