TODO:
Have you ever wanted to use log4net in your C# application?
SOLUTION:
To do so, you need to do 2 things:
Step 1: Add the following entry to the bottom of your AssemblyInfo.cs file
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
Step 2: Add this entry to your web or app.config file
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="c:\YOUR DIRECTORY HERE\your log file name.log" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<datePattern value="yyyyMMdd" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="30MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d %-5level %logger{1} : %message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>
NOTES:
If you already have a "configSections" entry, just add the new key to that section
TODO:
Have you ever wanted to write to a seperate log file per class for instance? In my case, I was running a Start() method of a class in a thread, and I was ending up with log messages in the single log file in an order that was really hard to follow. I wanted to write each class to it's own log file, and also ensure it was Thread safe while doing so. I was a little shocked at the lack of example out there on this topic, so below I have included a log class that wraps the log4net calls, and allows you to do the above.
SOLUTION:
using System;
using System.Collections.Generic;
using System.Text;
using log4net;
using log4net.Core;
using log4net.Config;
using log4net.Layout;
using log4net.Repository;
using log4net.Appender;
using log4net.Repository.Hierarchy;
namespace DevelopersAlley.Logging
{
public class ThreadSafeLogger
{
private const string _LOGNAME = "LogName";
static readonly object _locker = new object();
private ILog _logger;
/// <summary>
/// Allow a log file name to be passed in
/// </summary>
/// <param name="className"></param>
/// <param name="LogFileName"></param>
public ThreadSafeLogger(string ClassName, string LogPath)
{
if (!String.IsNullOrEmpty(LogPath))
{
//clean up path
if (!LogPath.EndsWith("\\"))
LogPath += "\\";
}
else
LogPath = "";
//make the nice log path
LogPath = String.Format("{0}{1}.log", LogPath, ClassName);
lock (_locker)
{
log4net.ThreadContext.Properties[_LOGNAME] = ClassName;
_logger = LogManager.GetLogger("Root");
if (!_logger.Logger.Repository.Configured)
XmlConfigurator.Configure();
//add the appender and set the filter now...
AddAppender(ClassName, CreateFileAppender(ClassName, LogPath));
}
}
#region LOG4NET UTILITIES
/// <summary>
/// Add an appender
/// </summary>
/// <param name="LoggerName"></param>
/// <param name="Appender"></param>
private void AddAppender(string LoggerName, log4net.Appender.IAppender Appender)
{
log4net.ILog log = log4net.LogManager.GetLogger(LoggerName);
log4net.Repository.Hierarchy.Logger l = (log4net.Repository.Hierarchy.Logger)log.Logger;
l.AddAppender(Appender);
}
/// <summary>
/// Find a named appender already attached to a logger
/// </summary>
/// <param name="appenderName"></param>
/// <returns></returns>
public log4net.Appender.IAppender FindAppender(string AppenderName)
{
foreach (log4net.Appender.IAppender appender in log4net.LogManager.GetRepository().GetAppenders())
{
if (appender.Name == AppenderName)
return appender;
}
return null;
}
/// <summary>
/// Create an appender
/// </summary>
/// <param name="name"></param>
/// <param name="fileName"></param>
/// <returns></returns>
public log4net.Appender.IAppender CreateFileAppender(string ClassName, string FileName)
{
log4net.Appender.RollingFileAppender appender = (log4net.Appender.RollingFileAppender)FindAppender(ClassName);
if (appender != null)
return appender;
appender = new log4net.Appender.RollingFileAppender();
appender.Name = ClassName;
appender.File = FileName;
appender.AppendToFile = true;
appender.MaximumFileSize = "10MB";
appender.MaxSizeRollBackups = 5;
appender.RollingStyle = RollingFileAppender.RollingMode.Size;
appender.StaticLogFileName = true;
//// add the filter for the log source
log4net.Filter.PropertyFilter filter = new log4net.Filter.PropertyFilter();
filter.Key = _LOGNAME;
filter.StringToMatch = ClassName;
filter.AcceptOnMatch = true;
//add deny all filter
log4net.Filter.DenyAllFilter filterDeny = new log4net.Filter.DenyAllFilter();
appender.AddFilter(filter);
appender.AddFilter(filterDeny);
log4net.Layout.PatternLayout layout = new log4net.Layout.PatternLayout();
layout.ConversionPattern = "%d [%t] %-5p %c - %m%n";
layout.ActivateOptions();
appender.Layout = layout;
appender.ActivateOptions();
log4net.Config.BasicConfigurator.Configure(appender);
return appender;
}
#endregion
#region INFO LOGGING
/// <summary>
/// Log a message to a particular log name
/// </summary>
/// <param name="messageFormat"></param>
/// <param name="classname"></param>
public void LogInfo(string MessageFormat, string ClassName)
{
lock (_locker)
{
log4net.ThreadContext.Properties[_LOGNAME] = ClassName;
if (_logger.IsInfoEnabled)
_logger.Info(MessageFormat);
}
}
#endregion
#region DEBUG LOGGING
/// <summary>
/// Log debug message to a file
/// </summary>
/// <param name="messageFormat"></param>
/// <param name="classname"></param>
public void LogDebug(string MessageFormat, string ClassName)
{
lock (_locker)
{
log4net.ThreadContext.Properties[_LOGNAME] = ClassName;
if (_logger.IsDebugEnabled)
_logger.Debug(MessageFormat);
}
}
#endregion
#region ERROR LOGGING
/// <summary>
/// Logs an error message to a specific log
/// </summary>
/// <param name="messageFormat"></param>
/// <param name="classname"></param>
public void LogError(string MessageFormat, string ClassName)
{
lock (_locker)
{
log4net.ThreadContext.Properties[_LOGNAME] = ClassName;
if (_logger.IsErrorEnabled)
_logger.Error(MessageFormat);
}
}
#endregion
}
}
NOTES:
- IMPORTANT** - in your app config, you now only need empty log4net node. Having any appenders in this node will throw things off, and you will end up with logs getting mixed up.
<log4net debug="true"></log4net>
- This creates appenders on the fly, and sets a PropertyFilter by the ClassName value that I passed in.
- Before the log event, you can see I set a property _LOGNAME, to ClassName. By setting this property, the filter will now only allow events to go to that Appender, because we applied a PropertyFilter to the Appender when we created it.
- I added locking to ensure Thread safety (may be overkill)
- To use, just call this code.
protected ThreadSafeLogger _Logger = null;
_Logger = new ThreadSafeLogger ("MyClassName", "C:\\logs");
- You will end up with a log file named C:\logs\MyClassName.log
- If you do #5 in each class with a different class name, you will end up with a log per class.
- Enjoy!