Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

Log4net: How to Setup and Use for the First Time

5.00/5 (6 votes)
20 Oct 2018CPOL6 min read 29.5K   411  
In this article, we will learn to setup log4net in our application for the very first time.

About Log4Net Usage

Log4net is a logging framework which is being used in .NET programming to log error/info/warnings to a variety of output targets to make it easy for the developer to understand code flow of a particular application in the production environment.

Why We Need to Use Logging Framework in Applications

When I started my career, I learnt debugging and resolved many bugs/defects by replicating the issue in the development environment. I used to read/debug code to understand the loopholes in the application.

One day, a bug was assigned to me and due to some configuration constraint, I was not able to replicate the issue. Later, I came to know about log files as I was asked by my team lead to look into it. I got that log file from the production environment and spent a few minutes to understand and find the exact error and later the code in the application, which was writing that error log. Then I got the use of log file.

As per my understanding, Log files are very helpful to figure out the condition/source code in the application which is resulting the problem in specific application-flow. It is very helpful when you cannot debug source code due to environmental constraint/not availability of source code or for any reason.

If proper logging is being done in code, then this logging can behave like good debugging information written in file/database, etc.

Get Log4Net

There are two sources available on the web to get log4net:

  1. Apache log4net
  2. logging-log4net On GitHub

I would prefer logging-log4net On GitHub over Apache log4net. The reason being that there are few issues still in unresolved state on log4net IssueTracker Jira reported for version Apache log4net and most of them have been resolved internally in version: logging-log4net On GitHub. Even I was using Apache version at the very first time, but got an issue: “RollingLogFileAppender doesn't use MaxSizeRollBackups setting, which is still in unresolved state since December 2013.

After downloading the complete branch, you need to open log4net.sln file located at path “logging-log4net-master\src\”. Just build the log4net project with “Release” configuration and get log4net.dll under path “logging-log4net-master\build\bin\net\4.5\release\”. Use this DLL to add as reference in the below steps in this tutorial.

How to Configure log4net in Application

We can configure log4net in the application by following the simple steps given below:

  1. Add log4net.dll as a reference in project.
  2. If you are looking to make an application use logging without being tightly dependent on a particular logging framework, then create an interface ILogger having methods like Info, Error, etc. and use dependency injection principle.
  3. Create a class, let’s say “SampleLog4NetLogger” by implementing ILogger interface.
  4. Add console application project in solution.
  5. Add config section for log4net in configuration (app.config/web.config) file of application.
  6. Add any appender node based on your storage type need in configuration file.
I will explain each step in detail here:
  • Create a class library project, let’s say “Common” in your solution and add log4net.dll as reference from folder “logging-log4net-master\build\bin\net\4.5\release\”.

    Image 1

  • If you are looking to make an application use logging without being tightly dependent on a particular logging framework, then create an interface ILogger having methods like Info, Error, etc. and use dependency injection principle.

    Image 2

    C#
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Common.Contract
    {
        public interface ILogger
        {
            #region Debug
            void Debug(object message);
            void Debug(object message, Exception exceptionData);
            #endregion
            #region Info
            void Info(object message);
            void Info(object message, Exception exceptionData);
            #endregion
            #region Warn
            void Warn(object message);
            void Warn(object message, Exception exceptionData);
            #endregion
            #region Error
            void Error(object message);
            void Error(object message, Exception exceptionData);
            #endregion
            #region Fatal
            void Fatal(object message);
            void Fatal(object message, Exception exceptionData);
            #endregion
        }
    }
  • Create a class let’s say “SampleLog4NetLogger” by implementing ILogger interface.
    1. Apart from implementing ILogger, add using directive for “log4net”.
    2. Add readonly field of type ILog and set it by calling LogManager.GetLogger result.
    3. Use this “ILog” type field to invoke appropriate logging method in each interface methods.
    4. You can also make this implementation as singleton. 
    C#
    using Common.Contract;
    using log4net;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Common.Implementation
    {
        public class SampleLog4NetLogger : ILogger
        {
            #region Fields
    
            private readonly ILog log4NetReal = LogManager.GetLogger(typeof(SampleLog4NetLogger));
            private static SampleLog4NetLogger thisLogger;
            #endregion
    
            #region Properties
    
            public static SampleLog4NetLogger Current
            {
                get
                {
                    if (thisLogger == null)
                    {
                        thisLogger = new SampleLog4NetLogger();
                    }
    
                    return thisLogger;
                }
            }
    
            #endregion
            
            #region Debug
            public void Debug(object message)
            {
                log4NetReal.Debug(message);
            }
    
            public void Debug(object message, Exception exceptionData)
            {
                log4NetReal.Debug(message, exceptionData);
            }
            #endregion
    
            #region Error
            public void Error(object message)
            {
                log4NetReal.Error(message);
            }
    
            public void Error(object message, Exception exceptionData)
            {
                log4NetReal.Error(message, exceptionData);
            }
            #endregion
    
            #region Fatal
            public void Fatal(object message)
            {
                log4NetReal.Fatal(message);
            }
    
            public void Fatal(object message, Exception exceptionData)
            {
                log4NetReal.Fatal(message, exceptionData);
            }
            #endregion
    
            #region Info
            public void Info(object message)
            {
                log4NetReal.Info(message);
            }
    
            public void Info(object message, Exception exceptionData)
            {
                log4NetReal.Info(message, exceptionData);
            }
            #endregion
    
            #region Warn
            public void Warn(object message)
            {
                log4NetReal.Warn(message);
            }
    
            public void Warn(object message, Exception exceptionData)
            {
                log4NetReal.Warn(message, exceptionData);
            } 
            #endregion
        }
    }
  • Add console application project in solution and add project reference of “Common” and “log4net.dll” reference in it.
  • Add config section for log4net in configuration (app.config/web.config) file of console application.
    XML
    <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
    </configSections>
  • Add any appender node based on your storage type need in configuration file.
    XML
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
      </configSections>
    
      <log4net threshold="ALL">
        <appender name="SampleRollingAppenderTest" type="log4net.Appender.RollingFileAppender">
          <param name="CountDirection" value="1"/>
          <param name="threshold" value="DEBUG"/>
          <datePattern>_dd_MMMM_yyyy_dddd_HH_mm</datePattern>
          <dateTimeStrategy type="log4net.Appender.RollingFileAppender+LocalDateTime"/>
          <maximumFileSize>100KB</maximumFileSize>
          <preserveLogFileNameExtension>true</preserveLogFileNameExtension>
          <rollingStyle>date</rollingStyle>
          <staticLogFileName>false</staticLogFileName>
          <appendToFile>true</appendToFile>
          <encoding>UTF-8</encoding>
          <file type="log4net.Util.PatternString" value="..\..\Logs\RahulLog.txt"/>
          <immediateFlush>true</immediateFlush>
          <layout type="log4net.Layout.DynamicPatternLayout">
            <header value="%newline**** Trace Opened Local: 
            %date{yyyy-MM-dd HH:mm:ss.fff} UTC: %utcdate{yyyy-MM-dd HH:mm:ss.fff} ****%newline"/>
            <footer value="**** Trace Closed %date{yyyy-MM-dd HH:mm:ss.fff} ****%newline"/>
            <conversionPattern value="%newline Logged Date : %date,%newline Exception : 
            %exception,%newline File : %file,%newline Location : %location,%newline Level : 
            %level,%newline Logged Message : %message,%newline Method Detail : 
            %method %newline ********************** %newline "/>
          </layout>
          <lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
        </appender>
        <root>
          <level value="DEBUG"/>
          <appender-ref ref="SampleRollingAppenderTest"/>
        </root>
      </log4net>
    
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
      </startup>
    </configuration>

Configuration Initialization

Manual Method-call Based Initialization

Now, this approach always works. You have to invoke method XmlConfigurator.ConfigureAndWatch to initialize log4net setup as per the config setup in your main entry method like main method of program class. Method “ConfigureAndWatch” configures log4net using the file specified, monitors the file for changes and reloads the configuration if a change is detected.

C#
XmlConfigurator.ConfigureAndWatch(new FileInfo(@"..\..\app.config"));

Attribute Based Initialization

This approach works only if the logger implementation “SampleLog4NetLogger”, which actually invokes method “LogManager.GetLogger” that is residing in main executable project. In this scenario, add XmlConfigurator attribute on assembly log4net in AssemblyInfo.cs file of the console application project at last. I have added another console application project named “SingleSetupExample” in the solution to show its usage.

C#
[assembly: log4net.Config.XmlConfigurator(ConfigFile = @"..\..\app.config", Watch = true)]

Image 3

In both approaches, in case you have created another configuration file for logger specific configuration, then you can give its relative path (with file name appended).

Remember, path of config file would be relative to EXE file of console application.

You can look into the bug regarding AssemblyInfo specific configuration of log4net at this link for more information on constraint on usage of attribute based initialization.

How to Use configured log4net in Application

  1. Create a class named “SimpleCalculator.cs” with constructor having parameter of type “ILogger” in Console application. Here, store this logger value and use it appropriately in its implementation.
    C#
    using Common.Contract;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CommonExampleApp
    {
        public class SimpleCalculator
        {
            private ILogger logger;
            private const string EntryFormat = "Starting Method {0} 
            with Left operand : {1} and Right Operand : {2}";
            private const string SuccessfulExitFormat = "Successfully Finishing Method {0} 
            with Left operand : {1}, Right Operand : {2}, Output : {3}";
            private const string FailedExitFormat = "Finishing with Failure in Method : {0} 
            with Left operand : {1} and Right Operand : {2}";
            private const string ParseFormat = "In Method {0} with Left operand : {1} 
            and Right Operand : {2}, Successfully Parsed {3} number.";
            private const string SuccessfulOutcomeFormat = "In Method {0} with Left operand : {1} 
            and Right Operand : {2}, Operation Successful with Output:  {3}.";
            private const string Left = "left";
            private const string Right = "right";
    
            public SimpleCalculator(ILogger _logger)
            {
                logger = _logger;
            }
    
            public double Add(string left, string right)
            {
                double result = 0;
                StackTrace trace = new StackTrace();
    
                logger.Info(string.Format
                (EntryFormat, trace.GetFrame(1).GetMethod().Name, left, right));
    
                try
                {
                    double left_Number = double.Parse(left);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Left));
    
                    double right_Number = double.Parse(right);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Right));
    
                    result = left_Number + right_Number;
                    logger.Debug(string.Format
                    (SuccessfulOutcomeFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format
                    (FailedExitFormat, trace.GetFrame(1).GetMethod().Name, left, right), ex);
                }
    
                logger.Info(string.Format
                (SuccessfulExitFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
    
                return result;
            }
    
            public double Subtract(string left, string right)
            {
                double result = 0;
                StackTrace trace = new StackTrace();
    
                logger.Info(string.Format(EntryFormat, trace.GetFrame(1).GetMethod().Name, left, right));
    
                try
                {
                    double left_Number = double.Parse(left);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Left));
    
                    double right_Number = double.Parse(right);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Right));
    
                    result = left_Number - right_Number;
                    logger.Debug(string.Format
                    (SuccessfulOutcomeFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format
                    (FailedExitFormat, trace.GetFrame(1).GetMethod().Name, left, right), ex);
                }
    
                logger.Info(string.Format
                (SuccessfulExitFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
    
                return result;
            }
    
            public double Multiply(string left, string right)
            {
                double result = 0;
                StackTrace trace = new StackTrace();
    
                logger.Info(string.Format
                (EntryFormat, trace.GetFrame(1).GetMethod().Name, left, right));
    
                try
                {
                    double left_Number = double.Parse(left);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Left));
    
                    double right_Number = double.Parse(right);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Right));
    
                    result = left_Number * right_Number;
                    logger.Debug(string.Format
                    (SuccessfulOutcomeFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format
                    (FailedExitFormat, trace.GetFrame(1).GetMethod().Name, left, right), ex);
                }
    
                logger.Info(string.Format
                (SuccessfulExitFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
    
                return result;
            }
    
            public double Divide(string left, string right)
            {
                double result = 0;
                StackTrace trace = new StackTrace();
    
                logger.Info(string.Format
                (EntryFormat, trace.GetFrame(1).GetMethod().Name, left, right));
    
                try
                {
                    double left_Number = double.Parse(left);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Left));
    
                    double right_Number = double.Parse(right);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Right));
    
                    result = left_Number / right_Number;
                    logger.Debug(string.Format
                    (SuccessfulOutcomeFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format
                    (FailedExitFormat, trace.GetFrame(1).GetMethod().Name, left, right), ex);
                }
    
                logger.Info(string.Format
                (SuccessfulExitFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
    
                return result;
            }
        }
    }
  2. In program.cs main method, create instance of this SimpleCalculator by passing instance of SampleLog4NetLogger and call its methods.
    C#
    using Common.Implementation;
    using log4net.Config;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CommonExampleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                XmlConfigurator.ConfigureAndWatch(new FileInfo(@"..\..\app.config"));
                SimpleCalculator calculator = 
                    new CommonExampleApp.SimpleCalculator(SampleLog4NetLogger.Current);
                double result = 0;
    
                SampleLog4NetLogger.Current.Debug("Adding two correct numbers....");
                result = calculator.Add("23", "56");
    
                SampleLog4NetLogger.Current.Debug("Adding two numbers with incorrect format....");
                result = calculator.Add("23", "Test");
    
                SampleLog4NetLogger.Current.Debug("Subtracting two correct numbers....");
                result = calculator.Subtract("1343", "56");
    
                SampleLog4NetLogger.Current.Debug("Subtracting two numbers with incorrect format....");
                result = calculator.Subtract("1343", "Robin");
    
                SampleLog4NetLogger.Current.Debug("Multiplying two correct numbers....");
                result = calculator.Multiply("43", "90");
    
                SampleLog4NetLogger.Current.Debug("Multiplying two numbers with incorrect format....");
                result = calculator.Multiply("43", "Cream");
    
                SampleLog4NetLogger.Current.Debug("Dividing two correct numbers....");
                result = calculator.Divide("225", "25");
    
                SampleLog4NetLogger.Current.Debug("Dividing two numbers with incorrect format....");
                result = calculator.Divide("225", "Number");
    
                SampleLog4NetLogger.Current.Debug("Dividing number by zero....");
                result = calculator.Divide("225", "0");
            }
        }
    }

Outcome of Logging

You can see the log file and logs in Logs folder like below:

Logged Date : 2018-08-11 18:52:45,620,
 Exception : ,
 File : C:\Users\USER1\OneDrive - Test Gaming\Attachments\Rahul Backup\
 General\Published\Example\Log4NetExamples\Common\Implementation\SampleLog4NetLogger.cs,
 Location : Common.Implementation.SampleLog4NetLogger.Debug
 (C:\Users\USER1\OneDrive - Test Gaming\Attachments\Rahul Backup\
 General\Published\Example\Log4NetExamples\Common\Implementation\SampleLog4NetLogger.cs:39),
 Level : DEBUG,
 Logged Message : Adding two correct numbers....,
 Method Detail : Debug

This is the output of logging.

Log4net supports logging in flat file, database table, on TCP client, console, etc. For each different storage, there is different appender. Most of them inherit few common properties, which can be used to specify logging message format, encoding, threshold, etc., which I will describe in each appender specific article.

Various Usages of Log4net

Log4net is used to log messages in various types of storage. Here is the list of main appenders, which are used widely in logging:

Usage Appender
When you have to store log messages in flat file or text file RollingFileAppender
When you have to store log messages in database(table) AdoNetAppender
When you have to pass log messages to Console output with colored specified for each log level ColoredConsoleAppender, AnsiColorTerminalAppender, ManagedColoredConsoleAppender
When you have to pass log messages to console ConsoleAppender
When you have to store log messages any trace listen (you can see logs in VS output window or store in other file formats like Excel/Access, etc.) DebugAppender, TraceAppender
When you have to send log messages in mail SmtpAppender
When you have to transfer log messages to telnet client TelnetAppender
When you have to transfer log messages to udp server UdpAppender
When you have to transfer log messages to remote server RemotingAppender
When you have to store log messages in system event logs to be watched by event viewer EventLogAppender
When you have to forward same log messages to various storages with buffering feature BufferingForwardingAppender
When you have to forward same log messages to various storages ForwardingAppender

Important Points

  1. In C# code, we prepare all log messages to be added in final log storage. For this, we create logger class to call log4net methods. This is logger type implementation specific feature.
  2. In configuration file, we filter those log messages (sent by C# code) and define the type and path for each of final log-message-storages just like text file, database, etc. This is appender specific feature.

Points of Interest

Log4net is a good framework for logging to track and troubleshoot the problem for code caught in production environment. I am using this since a long time and I love it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)