/*
<File>
<Copyright>Copyright © 2007, Daniel Vaughan. All rights reserved.</Copyright>
<License see="prj:///Documentation/License.txt"/>
<Owner Name="Daniel Vaughan" Email="dbvaughan@gmail.com"/>
<CreationDate>2007/11/19 20:00</CreationDate>
<LastSubmissionDate>$Date: $</LastSubmissionDate>
<Version>$Revision: $</Version>
<History>
<Revision Date="2008-06-25" ContributorName="Ladislav Burkovsky" ContributorEmail="burkovsky@freenet.de">
Population of Log4Net properties dictionary.</Revision>
</History>
</File>
*/
using System;
using System.Runtime.CompilerServices;
using log4net;
using log4net.Core;
namespace DanielVaughan.Logging.LogStrategies
{
/// <summary>
/// The <see cref="ILogStrategy"/> for Log4Net.
/// </summary>
class Log4NetStrategy : ILogStrategy
{
static readonly ILog defaultLog = LogManager.GetLogger(typeof(Log4NetStrategy));
/// <summary>
/// Logs the specified client log entry.
/// <seealso cref="IServerLogEntry"/>
/// </summary>
/// <param name="logEntry">The log entry.</param>
public void Write(IClientLogEntry logEntry)
{
ILog log = defaultLog;
if (logEntry.LogName != null)
{
log = LogManager.GetLogger(logEntry.LogName);
}
/* Create a Log4Net event data instance,
* and populate it with our log entry information. */
LoggingEventData data = new LoggingEventData();
if (logEntry.ExceptionMemento != null)
{ /* Use the exception memento to write
* the message and stack trace etc. */
data.ExceptionString = logEntry.ExceptionMemento.ToString();
}
data.Level = GetLog4NetLevel(logEntry.LogLevel);
ICodeLocation location = logEntry.CodeLocation;
if (location != null)
{
data.LocationInfo = new LocationInfo(location.ClassName, location.MethodName,
location.FileName, location.LineNumber.ToString());
}
data.LoggerName = logEntry.LogName;
data.Message = string.Format("{0}\nMachineName:{1}", logEntry.Message, logEntry.MachineName);
data.ThreadName = logEntry.ThreadName;
data.TimeStamp = logEntry.OccuredAt;
data.UserName = logEntry.UserName;
/* Copy custom properties into log4net Properties. */
if (logEntry.Properties != null && logEntry.Properties.Count > 0)
{
var properties = new log4net.Util.PropertiesDictionary();
foreach (var prop in logEntry.Properties)
{
properties[prop.Key] = prop.Value;
}
data.Properties = properties;
}
var loggingEvent = new LoggingEvent(data);
WriteThreadSafe(log.Logger, loggingEvent);
}
/// <summary>
/// Logs the specified server log entry.
/// <seealso cref="IServerLogEntry"/>
/// </summary>
/// <param name="logEntry">The log entry.</param>
public void Write(IServerLogEntry logEntry)
{
ILog log = defaultLog;
if (logEntry.LogName != null)
{
log = LogManager.GetLogger(logEntry.LogName);
}
LoggingEventData data = new LoggingEventData();
data.Domain = logEntry.AppDomain;
Exception exception = logEntry.Exception;
if (exception != null)
{
data.ExceptionString = string.Format("{0}{1}{2}",
exception, Environment.NewLine, exception.StackTrace);
}
data.Identity = logEntry.Identity;
data.Level = GetLog4NetLevel(logEntry.LogLevel);
ICodeLocation location = logEntry.CodeLocation;
if (location != null)
{
data.LocationInfo = new LocationInfo(location.ClassName, location.MethodName,
location.FileName, location.LineNumber.ToString());
}
data.LoggerName = logEntry.LogName;
data.Message = logEntry.Message;
data.ThreadName = logEntry.ThreadName;
data.TimeStamp = logEntry.OccuredAt;
data.UserName = logEntry.UserName;
/* Copy custom properties into log4net Properties. */
if (logEntry.Properties != null && logEntry.Properties.Count > 0)
{
var properties = new log4net.Util.PropertiesDictionary();
foreach (var prop in logEntry.Properties)
{
properties[prop.Key] = prop.Value;
}
data.Properties = properties;
}
var loggingEvent = new LoggingEvent(data);
WriteThreadSafe(log.Logger, loggingEvent);
}
/// <summary>
/// All Write log calls are done asynchronously because the DanielVaughan.Logging.Log
/// uses the AppPool to dispatch calls to this method. Therefore we need to ensure
/// that the call to Log is threadsafe. That is, if an appender such as FileAppender is used,
/// then we need to ensure it is called from no more than one thread at a time.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="loggingEvent">The logging event.</param>
[MethodImpl(MethodImplOptions.Synchronized)]
static void WriteThreadSafe(ILogger logger, LoggingEvent loggingEvent)
{
logger.Log(loggingEvent);
}
/// <summary>
/// Gets the log level for the log with the specified name.
/// </summary>
/// <param name="clientInfo">Caller information.</param>
/// <returns>The threshold log level.</returns>
public LogLevel GetLogLevel(IClientInfo clientInfo)
{
if (clientInfo == null)
{
return LogLevel;
}
ILog log = LogManager.GetLogger(clientInfo.LogName);
return GetLogLevel(log);
}
#region Default log level
LogLevel? lowestLogLevel;
readonly object lowestLogLevelLock = new object();
LogLevel LogLevel
{
get
{
if (!lowestLogLevel.HasValue)
{
lock (lowestLogLevelLock)
{
if (!lowestLogLevel.HasValue)
{
var loggers = LogManager.GetCurrentLoggers();
lowestLogLevel = LogLevel.None;
foreach (var log in loggers)
{
var logLevel = GetLogLevel(log);
if (logLevel < lowestLogLevel)
{
lowestLogLevel = logLevel;
}
}
}
}
}
return lowestLogLevel.Value;
}
}
#endregion
/// <summary>
/// Gets equivalent Clog log level of the log4net log.
/// </summary>
/// <param name="log">The log.</param>
/// <returns></returns>
static LogLevel GetLogLevel(ILog log)
{
if (log.IsDebugEnabled)
{
return LogLevel.Debug;
}
if (log.IsInfoEnabled)
{
return LogLevel.Info;
}
if (log.IsWarnEnabled)
{
return LogLevel.Warn;
}
if (log.IsErrorEnabled)
{
return LogLevel.Error;
}
if (log.IsFatalEnabled)
{
return LogLevel.Fatal;
}
return LogLevel.None;
}
/// <summary>
/// Converts a Clog LogLevel to the log4net level equivalent.
/// </summary>
/// <param name="level">The level to convert.</param>
/// <returns>The log4net level.</returns>
static Level GetLog4NetLevel(LogLevel level)
{
switch (level)
{
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:
return Level.Off;
}
}
}
}