Skip to content

Instantly share code, notes, and snippets.

@azzlack
Last active August 29, 2015 14:08
Show Gist options
  • Save azzlack/d5dfb5a2dbd134284dc8 to your computer and use it in GitHub Desktop.
Save azzlack/d5dfb5a2dbd134284dc8 to your computer and use it in GitHub Desktop.
Log4Net logger for Common.Logging
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
</configSections>
<common>
<logging>
<factoryAdapter type="{your-namespace}.Log4NetLoggerFactoryAdapter, {your-assembly-name}">
<arg key="configType" value="INLINE" />
</factoryAdapter>
</logging>
</common>
<log4net>
<appender name="FileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="App_Data\Root.log"/>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="2" />
<maximumFileSize value="1MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
</configuration>
namespace Common.Logging.Log4Net2
{
using System;
using System.Diagnostics;
using Common.Logging;
using Common.Logging.Factory;
using log4net.Core;
/// <summary>
/// Concrete implementation of <see cref="ILog"/> interface specific to log4net 1.2.10.
/// </summary>
/// <remarks>
/// Log4net is capable of outputting extended debug information about where the current
/// message was generated: class name, method name, file, line, etc. Log4net assumes that the location
/// information should be gathered relative to where Debug() was called.
/// When using Common.Logging, Debug() is called in Common.Logging.Log4Net.Log4NetLogger. This means that
/// the location information will indicate that Common.Logging.Log4Net.Log4NetLogger always made
/// the call to Debug(). We need to know where Common.Logging.ILog.Debug()
/// was called. To do this we need to use the log4net.ILog.Logger.Log method and pass in a Type telling
/// log4net where in the stack to begin looking for location information.
/// </remarks>
/// <author>Gilles Bayon</author>
/// <author>Erich Eichinger</author>
public class Log4NetLogger : AbstractLogger
{
#region Fields
private readonly ILogger _logger = null;
private static Type callerStackBoundaryType;
#endregion
/// <summary>
/// Constructor
/// </summary>
/// <param name="log"></param>
internal protected Log4NetLogger(ILoggerWrapper log)
{
this._logger = log.Logger;
}
#region ILog Members
/// <summary>
/// Checks if this logger is enabled for the <see cref="F:Common.Logging.LogLevel.Trace" /> level.
/// </summary>
public override bool IsTraceEnabled
{
get { return this._logger.IsEnabledFor(Level.Trace); }
}
/// <summary>
/// Checks if this logger is enabled for the <see cref="F:Common.Logging.LogLevel.Debug" /> level.
/// </summary>
public override bool IsDebugEnabled
{
get { return this._logger.IsEnabledFor(Level.Debug); }
}
/// <summary>
/// Checks if this logger is enabled for the <see cref="F:Common.Logging.LogLevel.Info" /> level.
/// </summary>
public override bool IsInfoEnabled
{
get { return this._logger.IsEnabledFor(Level.Info); }
}
/// <summary>
/// Checks if this logger is enabled for the <see cref="F:Common.Logging.LogLevel.Warn" /> level.
/// </summary>
public override bool IsWarnEnabled
{
get { return this._logger.IsEnabledFor(Level.Warn); }
}
/// <summary>
/// Checks if this logger is enabled for the <see cref="F:Common.Logging.LogLevel.Error" /> level.
/// </summary>
public override bool IsErrorEnabled
{
get { return this._logger.IsEnabledFor(Level.Error); }
}
/// <summary>
/// Checks if this logger is enabled for the <see cref="F:Common.Logging.LogLevel.Fatal" /> level.
/// </summary>
public override bool IsFatalEnabled
{
get { return this._logger.IsEnabledFor(Level.Fatal); }
}
/// <summary>
/// Sends the message to the underlying log4net system.
/// </summary>
/// <param name="logLevel">the level of this log event.</param>
/// <param name="message">the message to log</param>
/// <param name="exception">the exception to log (may be null)</param>
protected override void WriteInternal(LogLevel logLevel, object message, Exception exception)
{
// determine correct caller - this might change due to jit optimizations with method inlining
if (callerStackBoundaryType == null)
{
lock (this.GetType())
{
var stack = new StackTrace();
var thisType = this.GetType();
callerStackBoundaryType = typeof(AbstractLogger);
for (int i = 1; i < stack.FrameCount; i++)
{
if (!this.IsInTypeHierarchy(thisType, stack.GetFrame(i).GetMethod().DeclaringType))
{
callerStackBoundaryType = stack.GetFrame(i - 1).GetMethod().DeclaringType;
break;
}
}
}
}
Level level = GetLevel(logLevel);
this._logger.Log(callerStackBoundaryType, level, message, exception);
}
private bool IsInTypeHierarchy(Type currentType, Type checkType)
{
while (currentType != null && currentType != typeof(object))
{
if (currentType == checkType)
{
return true;
}
currentType = currentType.BaseType;
}
return false;
}
/// <summary>
/// Maps <see cref="LogLevel"/> to log4net's <see cref="Level"/>
/// </summary>
/// <param name="logLevel"></param>
public static Level GetLevel(LogLevel logLevel)
{
switch (logLevel)
{
case LogLevel.All:
return Level.All;
case LogLevel.Trace:
return Level.Trace;
case LogLevel.Debug:
return Level.Debug;
case LogLevel.Info:
return Level.Info;
case LogLevel.Warn:
return Level.Warn;
case LogLevel.Error:
return Level.Error;
case LogLevel.Fatal:
return Level.Fatal;
default:
throw new ArgumentOutOfRangeException("logLevel", logLevel, "unknown log level");
}
}
#endregion
}
}
namespace Common.Logging.Log4Net2
{
using System;
using System.IO;
using Common.Logging;
using Common.Logging.Configuration;
using Common.Logging.Factory;
using log4net.Config;
/// <summary>
/// Concrete subclass of ILoggerFactoryAdapter specific to log4net 1.2.10.
/// </summary>
/// <remarks>
/// The following configuration property values may be configured:
/// <list type="bullet">
/// <item><c>configType</c>: <c>INLINE|FILE|FILE-WATCH|EXTERNAL</c></item>
/// <item><c>configFile</c>: log4net configuration file path in case of FILE or FILE-WATCH</item>
/// </list>
/// The configType values have the following implications:
/// <list type="bullet">
/// <item>INLINE: simply calls <c>XmlConfigurator.Configure()</c></item>
/// <item>FILE: calls <c>XmlConfigurator.Configure(System.IO.FileInfo)</c> using <c>configFile</c>.</item>
/// <item>FILE-WATCH: calls <c>XmlConfigurator.ConfigureAndWatch(System.IO.FileInfo)</c> using <c>configFile</c>.</item>
/// <item>EXTERNAL: does nothing and expects log4net to be configured elsewhere.</item>
/// <item>&lt;any&gt;: calls <c>BasicConfigurator.Configure()</c></item>
/// </list>
/// </remarks>
/// <example>
/// The following snippet shows an example of how to configure log4net logging with Common.Logging:
/// <code>
/// &lt;configuration&gt;
/// &lt;configSections&gt;
/// &lt;sectionGroup name=&quot;common&quot;&gt;
/// &lt;section name=&quot;logging&quot;
/// type=&quot;Common.Logging.ConfigurationSectionHandler, Common.Logging&quot;
/// requirePermission=&quot;false&quot; /&gt;
/// &lt;/sectionGroup&gt;
/// &lt;section name=&quot;log4net&quot;
/// type=&quot;log4net.Config.Log4NetConfigurationSectionHandler&quot;
/// requirePermission=&quot;false&quot; /&gt;
/// &lt;/configSections&gt;
///
/// &lt;common&gt;
/// &lt;logging&gt;
/// &lt;factoryAdapter type=&quot;Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net&quot;&gt;
/// &lt;arg key=&quot;level&quot; value=&quot;ALL&quot; /&gt;
/// &lt;arg key=&quot;configType&quot; value=&quot;INLINE&quot; /&gt;
/// &lt;/factoryAdapter&gt;
/// &lt;/logging&gt;
/// &lt;/common&gt;
///
/// &lt;log4net debug=&quot;false&quot;&gt;
///
/// &lt;appender name=&quot;RollingLogFileAppender&quot; type=&quot;log4net.Appender.RollingFileAppender, log4net&quot;&gt;
/// &lt;param name=&quot;File&quot; value=&quot;./Web.log&quot; /&gt;
/// &lt;param name=&quot;AppendToFile&quot; value=&quot;true&quot; /&gt;
/// &lt;param name=&quot;MaxSizeRollBackups&quot; value=&quot;1&quot; /&gt;
/// &lt;param name=&quot;MaximumFileSize&quot; value=&quot;1GB&quot; /&gt;
/// &lt;param name=&quot;RollingStyle&quot; value=&quot;Date&quot; /&gt;
/// &lt;param name=&quot;StaticLogFileName&quot; value=&quot;false&quot; /&gt;
///
/// &lt;layout type=&quot;log4net.Layout.PatternLayout, log4net&quot;&gt;
/// &lt;param name=&quot;ConversionPattern&quot; value=&quot;%d [%t] %-5p %c - %m%n&quot; /&gt;
/// &lt;/layout&gt;
///
/// &lt;/appender&gt;
///
/// &lt;appender name=&quot;TraceAppender&quot; type=&quot;log4net.Appender.TraceAppender&quot;&gt;
/// &lt;layout type=&quot;log4net.Layout.PatternLayout&quot;&gt;
/// &lt;param name=&quot;ConversionPattern&quot; value=&quot;%-5p: %m&quot; /&gt;
/// &lt;/layout&gt;
/// &lt;/appender&gt;
///
/// &lt;root&gt;
/// &lt;level value=&quot;ALL&quot; /&gt;
/// &lt;appender-ref ref=&quot;TraceAppender&quot; /&gt;
/// &lt;appender-ref ref=&quot;RollingLogFileAppender&quot; /&gt;
/// &lt;/root&gt;
///
/// &lt;/log4net&gt;
/// &lt;/configuration&gt;
/// </code>
/// </example>
/// <author>Gilles Bayon</author>
/// <author>Erich Eichinger</author>
public class Log4NetLoggerFactoryAdapter : AbstractCachingLoggerFactoryAdapter
{
/// <summary>
/// Abstract interface to the underlying log4net runtime
/// </summary>
public interface ILog4NetRuntime
{
/// <summary>Calls <see cref="XmlConfigurator.Configure()"/></summary>
void XmlConfiguratorConfigure();
/// <summary>Calls <see cref="XmlConfigurator.Configure(System.IO.FileInfo)"/></summary>
void XmlConfiguratorConfigure(string configFile);
/// <summary>Calls <see cref="XmlConfigurator.ConfigureAndWatch(System.IO.FileInfo)"/></summary>
void XmlConfiguratorConfigureAndWatch(string configFile);
/// <summary>Calls <see cref="BasicConfigurator.Configure()"/></summary>
void BasicConfiguratorConfigure();
/// <summary>Calls <see cref="LogManager.GetLogger(string)"/></summary>
log4net.ILog GetLogger(string name);
}
private class Log4NetRuntime : ILog4NetRuntime
{
public void XmlConfiguratorConfigure()
{
XmlConfigurator.Configure();
}
public void XmlConfiguratorConfigure(string configFile)
{
XmlConfigurator.Configure(new FileInfo(configFile));
}
public void XmlConfiguratorConfigureAndWatch(string configFile)
{
XmlConfigurator.ConfigureAndWatch(new FileInfo(configFile));
}
public void BasicConfiguratorConfigure()
{
BasicConfigurator.Configure();
}
public log4net.ILog GetLogger(string name)
{
return log4net.LogManager.GetLogger(name);
}
}
private readonly ILog4NetRuntime _runtime;
/// <summary>
/// Constructor
/// </summary>
/// <param name="properties">configuration properties, see <see cref="Log4NetLoggerFactoryAdapter"/> for more.</param>
public Log4NetLoggerFactoryAdapter(Common.Logging.Configuration.NameValueCollection properties)
: this(properties, new Log4NetRuntime())
{ }
/// <summary>
/// Constructor accepting configuration properties and an arbitrary
/// <see cref="ILog4NetRuntime"/> instance.
/// </summary>
/// <param name="properties">configuration properties, see <see cref="Log4NetLoggerFactoryAdapter"/> for more.</param>
/// <param name="runtime">a log4net runtime adapter</param>
protected Log4NetLoggerFactoryAdapter(Common.Logging.Configuration.NameValueCollection properties, ILog4NetRuntime runtime)
: base(true)
{
if (runtime == null)
{
throw new ArgumentNullException("runtime");
}
this._runtime = runtime;
// parse config properties
string configType = ArgUtils.GetValue(properties, "configType", string.Empty).ToUpper();
string configFile = ArgUtils.GetValue(properties, "configFile", string.Empty);
// app-relative path?
if (configFile.StartsWith("~/") || configFile.StartsWith("~\\"))
{
configFile = string.Format("{0}/{1}", AppDomain.CurrentDomain.BaseDirectory.TrimEnd('/', '\\'), configFile.Substring(2));
}
if (configType == "FILE" || configType == "FILE-WATCH")
{
if (configFile == string.Empty)
{
throw new ConfigurationException("Configuration property 'configFile' must be set for log4Net configuration of type 'FILE' or 'FILE-WATCH'.");
}
if (!File.Exists(configFile))
{
throw new ConfigurationException("log4net configuration file '" + configFile + "' does not exists");
}
}
switch (configType)
{
case "INLINE":
this._runtime.XmlConfiguratorConfigure();
break;
case "FILE":
this._runtime.XmlConfiguratorConfigure(configFile);
break;
case "FILE-WATCH":
this._runtime.XmlConfiguratorConfigureAndWatch(configFile);
break;
case "EXTERNAL":
// Log4net will be configured outside of Common.Logging
break;
default:
this._runtime.BasicConfiguratorConfigure();
break;
}
}
/// <summary>
/// Create a ILog instance by name
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
protected override ILog CreateLogger(string name)
{
return new Log4NetLogger(this._runtime.GetLogger(name));
}
}
}
<packages>
<package id="Common.Logging" version="2.2.0" targetFramework="net451" />
<package id="Common.Logging.Core" version="2.2.0" targetFramework="net451" />
<package id="log4net" version="2.0.3" targetFramework="net451" />
</packages>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment