C# – Change log4net Log Path and Level Programmatically

with 4 comments

Log4Net makes it incredibly easy to implement logging functionality in your application. Log4Net configuration can be little bit tricky, but its easy to learn.

Most of the time when I am working on a project the application configurations including log path and level are fetched from the database.

Log4Net doesn’t provide any straight forward way of fetching log file path or level from database (because its not necessary). To change the log file path and level programmatically we have to write our own log file appender.

For this demonstration I will write one rolling file appender. The custom file appender class should inherit from the log4net’s RollingFileAppender class.  We have to override the File property of the appender and change the log path. My rolling file appender class looks like this:

My Rolling File Appender:

namespace MyLog4Net.LogAppenders
{
    using System;

    using log4net;
    using log4net.Appender;

    using MyLog4Net.Interfaces;
    using MyLog4Net.Services;

    /// <summary>
    /// My log4net Rolling file appender.
    /// </summary>
    public class MyLog4NetFileAppender : RollingFileAppender
    {
        #region Constants and Fields

        /// <summary>
        /// Component parameter business layer
        /// </summary>
        private static IConfigService service;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MyLog4NetFileAppender"/> class.
        /// </summary>
        public MyLog4NetFileAppender()
            : this(new ConfigService())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MyLog4NetFileAppender"/> class.
        /// </summary>
        /// <param name="configService">
        /// The config service.
        /// </param>
        public MyLog4NetFileAppender(IConfigService configService)
        {
            service = configService;

            // get the log level
            // must be a proper log4net Threshold
            string logLevel = service.GetLogLevel();

            // set the log level
            LogManager.GetRepository().Threshold = LogManager.GetRepository().LevelMap[logLevel];
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the log file name.
        /// </summary>
        /// <value>The log file name.</value>
        public override string File
        {
            get
            {
                return base.File;
            }

            set
            {
                try
                {
                    // get the log directory
                    string logDirectory = service.GetLogPath();

                    // get the log file name from the config file.
                    string logFileName = value.Substring(value.LastIndexOf('\\') + 1);

                    // build the new log path
                    if (!logDirectory.EndsWith("\\") || !logDirectory.EndsWith("/"))
                    {
                        logDirectory += "\\";
                    }

                    // replace the new log file path
                    base.File = logDirectory + logFileName;
                }
                catch (Exception ex)
                {
                    // TODO: Log the error
                    // use the default
                    base.File = value;
                }
            }
        }

        #endregion
    }
}

Setting The Log Level (Threshold):

public MyLog4NetFileAppender(IConfigService configService)
{
    // Service layer that will be used to fetch config data
    service = configService;

    // get the log level
    // must be a proper log4net Threshold
    string logLevel = service.GetLogLevel();

    // set the log level
    LogManager.GetRepository().Threshold = LogManager.GetRepository().LevelMap[logLevel];
}

In the constructor of this class I fetch the log threshold configured in the database using the service.GetLogLevel() method. The LogManager.GetRepository() returns the default log4net repository of the calling assembly. LogManager.GetRepository().LevelMap gives us a set of default log4net levels. We change the log level by pasing the value fetched from the database to the current repositories Threshold property.

Setting The Log Path:

public override string File
{
    get
    {
        return base.File;
    }

    set
    {
        try
        {
            // get the log directory
            string logDirectory = service.GetLogPath();

            // get the log file name from the config file.
            string logFileName = value.Substring(value.LastIndexOf('\\') + 1);

            // build the new log path
            if (!logDirectory.EndsWith("\\") || !logDirectory.EndsWith("/"))
            {
                logDirectory += "\\";
            }

            // replace the new log file path
            base.File = logDirectory + logFileName;
        }
        catch (Exception ex)
        {
            // TODO: Log the error
            // use the default
            base.File = value;
        }
    }
}

When log4net is initializing the File property of the file appender would be called. Here the value would be the file name that was set in the log4net configuration file. In this property we extract the file name and then append the log path that we fetched from the database.

Using Our Custom Appender:

To use our custom appender we would create a log4net appender section in the config file and use our appender in the appender type. I like placing my log4net configuration is a different file. My log4net configuration file looks like:

Log4Net Configuration File:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

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

    <!-- log4net -->
    <log4net>
        <!-- Define some output appenders -->
        <appender name="MyRollingFileAppender" type="MyLog4Net.LogAppenders.MyLog4NetFileAppender">
            <param name="File" value="..\\Log\\MyApplication-log-file.txt" />
            <param name="AppendToFile" value="true" />
            <param name="RollingStyle" value="Size" />
            <param name="StaticLogFileName" value="true" />
            <layout type="log4net.Layout.PatternLayout">
                <param name="Header" value="[Header]&#13;&#10;" />
                <param name="Footer" value="[Footer]&#13;&#10;" />
                <param name="ConversionPattern" value="%d [%t] %-5p %C %M - %m%n" />
            </layout>
        </appender>
        <!-- Setup the root category, add the appenders and set the default level -->
        <!-- Setup the root category, add the appenders and set the default level
                ALL
                DEBUG
                INFO
                WARN
                ERROR
                FATAL
                OFF
            For example, setting the threshold of an appender to DEBUG will also allow INFO,
            WARN, ERROR and FATAL messages to log along with DEBUG messages. (DEBUG is the
            lowest level). This is usually acceptable as there is little use for DEBUG
            messages without the surrounding INFO, WARN, ERROR and FATAL messages.
            Similarly, setting the threshold of an appender to ERROR will filter out DEBUG,
            INFO and ERROR messages but not FATAL messages.
        -->
        <root>
            <level value="ALL" />
            <appender-ref ref="MyRollingFileAppender" />
        </root>
    </log4net>
</configuration>

Notice that instead of using log4net’s RollingFileAppender class I am using our MyLog4NetFileAppender class.

Initializing Log4Net:

To initialize log4net put the following code in the AssemblyInfo.cs file.

/* Configure log4net
   log4net configuration will be searched in the .config file*/
[assembly: XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]

The above code tells log4net to look for log4net.config file in the application and initialize itself.

Now everything is ready you can now run your application. To check if the code works or not you can put breakpoints in the constructor and File property of the appender.

Source:

Download the source code for this article MyLog4Net.zip

Possible Related Posts:

  1. Writing Provider Independent .NET Data Access Code
  2. .NET Type Forwarding – Moving Types Between Assemblies
  3. C# Obtain List Of SQL Server Instances

Written by Zuhaib

November 10th, 2009 at 11:07 am

4 Responses to 'C# – Change log4net Log Path and Level Programmatically'

Subscribe to comments with RSS or TrackBack to 'C# – Change log4net Log Path and Level Programmatically'.

  1. How do i split up the log entry and categorize the fields? And what if there are some additional patterns like header and footers?

    I know how to read a file but only concern is splitting the entry and categorize the fields.

    Any code snippet will help a lot?

    Regards,
    Nash

    [ Reply ]

    Zuhaib Reply:

    You can have multiple appenders that append to different log files.

    You can get a specific appender like this.

    ILog errorLogger = LogManager.GetLogger(“MyErrorLogFileAppender”);

    What do you mean by categorize the fields?

    The Header and Footer param specified in the Appender’s Layout would be printed everytime you application starts and ends.

    [ Reply ]

    Birzhan Reply:

    @Zuhaib,
    what if you need to create a new log file every day?
    Will you create multiple configurations for next couple of hundred yours?

    [ Reply ]

    Nash

    18 Feb 10 at 5:36 pm

  2. This is great, exactly what I was looking for.

    Only thing I’m not sure of is how to actually use it, haha.

    How do I go about actually writing out log data? Ideally, I would like to have the log files written relative to a specified path by the user via text box.

    Thank you!

    - Jeff

    [ Reply ]

    Jeff

    8 Jul 10 at 10:58 am

Leave a Reply