//=============================================================================== // LibLog // // https://github.com/damianh/LibLog //=============================================================================== // Copyright © 2011-2014 Damian Hickey. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //=============================================================================== #pragma warning disable 1591 namespace DotNetOpenAuth.Logging { using System.Collections.Generic; using DotNetOpenAuth.Logging.LogProviders; using System; using System.Diagnostics; using System.Globalization; /// /// Simple interface that represent a logger. /// public interface ILog { /// /// Log a message the specified log level. /// /// The log level. /// The message function. /// An optional exception. /// true if the message was logged. Otherwise false. /// /// Note to implementers: the message func should not be called if the loglevel is not enabled /// so as not to incur performance penalties. /// /// To check IsEnabled call Log with only LogLevel and check the return value, no event will be written /// bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null); } /// /// The log level. /// public enum LogLevel { Trace, Debug, Info, Warn, Error, Fatal } public static class LogExtensions { public static bool IsDebugEnabled(this ILog logger) { GuardAgainstNullLogger(logger); return logger.Log(LogLevel.Debug, null); } public static bool IsErrorEnabled(this ILog logger) { GuardAgainstNullLogger(logger); return logger.Log(LogLevel.Error, null); } public static bool IsFatalEnabled(this ILog logger) { GuardAgainstNullLogger(logger); return logger.Log(LogLevel.Fatal, null); } public static bool IsInfoEnabled(this ILog logger) { GuardAgainstNullLogger(logger); return logger.Log(LogLevel.Info, null); } public static bool IsTraceEnabled(this ILog logger) { GuardAgainstNullLogger(logger); return logger.Log(LogLevel.Trace, null); } public static bool IsWarnEnabled(this ILog logger) { GuardAgainstNullLogger(logger); return logger.Log(LogLevel.Warn, null); } public static void Debug(this ILog logger, Func messageFunc) { GuardAgainstNullLogger(logger); logger.Log(LogLevel.Debug, messageFunc); } public static void Debug(this ILog logger, string message) { if (logger.IsDebugEnabled()) { logger.Log(LogLevel.Debug, message.AsFunc()); } } public static void DebugFormat(this ILog logger, string message, params object[] args) { if (logger.IsDebugEnabled()) { logger.LogFormat(LogLevel.Debug, message, args); } } public static void DebugException(this ILog logger, string message, Exception exception) { if (logger.IsDebugEnabled()) { logger.Log(LogLevel.Debug, message.AsFunc(), exception); } } public static void Error(this ILog logger, Func messageFunc) { logger.Log(LogLevel.Error, messageFunc); } public static void Error(this ILog logger, string message) { if (logger.IsErrorEnabled()) { logger.Log(LogLevel.Error, message.AsFunc()); } } public static void ErrorFormat(this ILog logger, string message, params object[] args) { if (logger.IsErrorEnabled()) { logger.LogFormat(LogLevel.Error, message, args); } } public static void ErrorException(this ILog logger, string message, Exception exception) { if (logger.IsErrorEnabled()) { logger.Log(LogLevel.Error, message.AsFunc(), exception); } } public static void Fatal(this ILog logger, Func messageFunc) { logger.Log(LogLevel.Fatal, messageFunc); } public static void Fatal(this ILog logger, string message) { if (logger.IsFatalEnabled()) { logger.Log(LogLevel.Fatal, message.AsFunc()); } } public static void FatalFormat(this ILog logger, string message, params object[] args) { if (logger.IsFatalEnabled()) { logger.LogFormat(LogLevel.Fatal, message, args); } } public static void FatalException(this ILog logger, string message, Exception exception) { if (logger.IsFatalEnabled()) { logger.Log(LogLevel.Fatal, message.AsFunc(), exception); } } public static void Info(this ILog logger, Func messageFunc) { GuardAgainstNullLogger(logger); logger.Log(LogLevel.Info, messageFunc); } public static void Info(this ILog logger, string message) { if (logger.IsInfoEnabled()) { logger.Log(LogLevel.Info, message.AsFunc()); } } public static void InfoFormat(this ILog logger, string message, params object[] args) { if (logger.IsInfoEnabled()) { logger.LogFormat(LogLevel.Info, message, args); } } public static void InfoException(this ILog logger, string message, Exception exception) { if (logger.IsInfoEnabled()) { logger.Log(LogLevel.Info, message.AsFunc(), exception); } } public static void Trace(this ILog logger, Func messageFunc) { GuardAgainstNullLogger(logger); logger.Log(LogLevel.Trace, messageFunc); } public static void Trace(this ILog logger, string message) { if (logger.IsTraceEnabled()) { logger.Log(LogLevel.Trace, message.AsFunc()); } } public static void TraceFormat(this ILog logger, string message, params object[] args) { if (logger.IsTraceEnabled()) { logger.LogFormat(LogLevel.Trace, message, args); } } public static void TraceException(this ILog logger, string message, Exception exception) { if (logger.IsTraceEnabled()) { logger.Log(LogLevel.Trace, message.AsFunc(), exception); } } public static void Warn(this ILog logger, Func messageFunc) { GuardAgainstNullLogger(logger); logger.Log(LogLevel.Warn, messageFunc); } public static void Warn(this ILog logger, string message) { if (logger.IsWarnEnabled()) { logger.Log(LogLevel.Warn, message.AsFunc()); } } public static void WarnFormat(this ILog logger, string message, params object[] args) { if (logger.IsWarnEnabled()) { logger.LogFormat(LogLevel.Warn, message, args); } } public static void WarnException(this ILog logger, string message, Exception exception) { if (logger.IsWarnEnabled()) { logger.Log(LogLevel.Warn, message.AsFunc(), exception); } } private static void GuardAgainstNullLogger(ILog logger) { if (logger == null) { throw new ArgumentNullException("logger"); } } private static void LogFormat(this ILog logger, LogLevel logLevel, string message, params object[] args) { var result = string.Format(CultureInfo.InvariantCulture, message, args); logger.Log(logLevel, result.AsFunc()); } // Avoid the closure allocation, see https://gist.github.com/AArnott/d285feef75c18f6ecd2b private static Func AsFunc(this T value) where T : class { return value.Return; } private static T Return(this T value) { return value; } } /// /// Represents a way to get a /// public interface ILogProvider { ILog GetLogger(string name); } /// /// Provides a mechanism to create instances of objects. /// public static class LogProvider { private static ILogProvider _currentLogProvider; /// /// Gets a logger for the specified type. /// /// The type whose name will be used for the logger. /// An instance of public static ILog For() { return GetLogger(typeof(T)); } /// /// Gets a logger for the current class. /// /// An instance of public static ILog GetCurrentClassLogger() { var stackFrame = new StackFrame(1, false); return GetLogger(stackFrame.GetMethod().DeclaringType); } /// /// Gets a logger for the specified type. /// /// The type whose name will be used for the logger. /// An instance of public static ILog GetLogger(Type type) { return GetLogger(type.FullName); } /// /// Gets a logger with the specified name. /// /// The name. /// An instance of public static ILog GetLogger(string name) { ILogProvider logProvider = _currentLogProvider ?? ResolveLogProvider(); return logProvider == null ? new NoOpLogger() : (ILog)new LoggerExecutionWrapper(logProvider.GetLogger(name)); } /// /// Sets the current log provider. /// /// The log provider. public static void SetCurrentLogProvider(ILogProvider logProvider) { _currentLogProvider = logProvider; } public delegate bool IsLoggerAvailable(); public delegate ILogProvider CreateLogProvider(); public static readonly List> LogProviderResolvers = new List> { new Tuple(SerilogLogProvider.IsLoggerAvailable, () => new SerilogLogProvider()), new Tuple(NLogLogProvider.IsLoggerAvailable, () => new NLogLogProvider()), new Tuple(Log4NetLogProvider.IsLoggerAvailable, () => new Log4NetLogProvider()), new Tuple(EntLibLogProvider.IsLoggerAvailable, () => new EntLibLogProvider()), new Tuple(LoupeLogProvider.IsLoggerAvailable, () => new LoupeLogProvider()) }; private static ILogProvider ResolveLogProvider() { try { foreach(var providerResolver in LogProviderResolvers) { if(providerResolver.Item1()) { return providerResolver.Item2(); } } } catch (Exception ex) { Console.WriteLine( "Exception occured resolving a log provider. Logging for this assembly {0} is disabled. {1}", typeof(LogProvider).Assembly.FullName, ex); } return null; } public class NoOpLogger : ILog { public bool Log(LogLevel logLevel, Func messageFunc, Exception exception) { return false; } } } public class LoggerExecutionWrapper : ILog { private readonly ILog _logger; public const string FailedToGenerateLogMessage = "Failed to generate log message"; public ILog WrappedLogger { get { return _logger; } } public LoggerExecutionWrapper(ILog logger) { _logger = logger; } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null) { if (messageFunc == null) { return _logger.Log(logLevel, null); } Func wrappedMessageFunc = () => { try { return messageFunc(); } catch (Exception ex) { Log(LogLevel.Error, () => FailedToGenerateLogMessage, ex); } return null; }; return _logger.Log(logLevel, wrappedMessageFunc, exception); } } } namespace DotNetOpenAuth.Logging.LogProviders { using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading; public class NLogLogProvider : ILogProvider { private readonly Func _getLoggerByNameDelegate; private static bool _providerIsAvailableOverride = true; public NLogLogProvider() { if (!IsLoggerAvailable()) { throw new InvalidOperationException("NLog.LogManager not found"); } _getLoggerByNameDelegate = GetGetLoggerMethodCall(); } public static bool ProviderIsAvailableOverride { get { return _providerIsAvailableOverride; } set { _providerIsAvailableOverride = value; } } public ILog GetLogger(string name) { return new NLogLogger(_getLoggerByNameDelegate(name)); } public static bool IsLoggerAvailable() { return ProviderIsAvailableOverride && GetLogManagerType() != null; } private static Type GetLogManagerType() { return Type.GetType("NLog.LogManager, NLog"); } private static Func GetGetLoggerMethodCall() { Type logManagerType = GetLogManagerType(); MethodInfo method = logManagerType.GetMethod("GetLogger", new[] { typeof(string) }); ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); MethodCallExpression methodCall = Expression.Call(null, method, new Expression[] { nameParam }); return Expression.Lambda>(methodCall, new[] { nameParam }).Compile(); } public class NLogLogger : ILog { private readonly dynamic _logger; internal NLogLogger(dynamic logger) { _logger = logger; } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception) { if (messageFunc == null) { return IsLogLevelEnable(logLevel); } if(exception != null) { return LogException(logLevel, messageFunc, exception); } switch (logLevel) { case LogLevel.Debug: if (_logger.IsDebugEnabled) { _logger.Debug(messageFunc()); return true; } break; case LogLevel.Info: if (_logger.IsInfoEnabled) { _logger.Info(messageFunc()); return true; } break; case LogLevel.Warn: if (_logger.IsWarnEnabled) { _logger.Warn(messageFunc()); return true; } break; case LogLevel.Error: if (_logger.IsErrorEnabled) { _logger.Error(messageFunc()); return true; } break; case LogLevel.Fatal: if (_logger.IsFatalEnabled) { _logger.Fatal(messageFunc()); return true; } break; default: if (_logger.IsTraceEnabled) { _logger.Trace(messageFunc()); return true; } break; } return false; } private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) { switch (logLevel) { case LogLevel.Debug: if (_logger.IsDebugEnabled) { _logger.DebugException(messageFunc(), exception); return true; } break; case LogLevel.Info: if (_logger.IsInfoEnabled) { _logger.InfoException(messageFunc(), exception); return true; } break; case LogLevel.Warn: if (_logger.IsWarnEnabled) { _logger.WarnException(messageFunc(), exception); return true; } break; case LogLevel.Error: if (_logger.IsErrorEnabled) { _logger.ErrorException(messageFunc(), exception); return true; } break; case LogLevel.Fatal: if (_logger.IsFatalEnabled) { _logger.FatalException(messageFunc(), exception); return true; } break; default: if (_logger.IsTraceEnabled) { _logger.TraceException(messageFunc(), exception); return true; } break; } return false; } private bool IsLogLevelEnable(LogLevel logLevel) { switch (logLevel) { case LogLevel.Debug: return _logger.IsDebugEnabled; case LogLevel.Info: return _logger.IsInfoEnabled; case LogLevel.Warn: return _logger.IsWarnEnabled; case LogLevel.Error: return _logger.IsErrorEnabled; case LogLevel.Fatal: return _logger.IsFatalEnabled; default: return _logger.IsTraceEnabled; } } } } public class Log4NetLogProvider : ILogProvider { private readonly Func _getLoggerByNameDelegate; private static bool _providerIsAvailableOverride = true; public Log4NetLogProvider() { if (!IsLoggerAvailable()) { throw new InvalidOperationException("log4net.LogManager not found"); } _getLoggerByNameDelegate = GetGetLoggerMethodCall(); } public static bool ProviderIsAvailableOverride { get { return _providerIsAvailableOverride; } set { _providerIsAvailableOverride = value; } } public ILog GetLogger(string name) { return new Log4NetLogger(_getLoggerByNameDelegate(name)); } public static bool IsLoggerAvailable() { return ProviderIsAvailableOverride && GetLogManagerType() != null; } private static Type GetLogManagerType() { return Type.GetType("log4net.LogManager, log4net"); } private static Func GetGetLoggerMethodCall() { Type logManagerType = GetLogManagerType(); MethodInfo method = logManagerType.GetMethod("GetLogger", new[] { typeof(string) }); ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); MethodCallExpression methodCall = Expression.Call(null, method, new Expression[] { nameParam }); return Expression.Lambda>(methodCall, new[] { nameParam }).Compile(); } public class Log4NetLogger : ILog { private readonly dynamic _logger; internal Log4NetLogger(dynamic logger) { _logger = logger; } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception) { if (messageFunc == null) { return IsLogLevelEnable(logLevel); } if (exception != null) { return LogException(logLevel, messageFunc, exception); } switch (logLevel) { case LogLevel.Info: if (_logger.IsInfoEnabled) { _logger.Info(messageFunc()); return true; } break; case LogLevel.Warn: if (_logger.IsWarnEnabled) { _logger.Warn(messageFunc()); return true; } break; case LogLevel.Error: if (_logger.IsErrorEnabled) { _logger.Error(messageFunc()); return true; } break; case LogLevel.Fatal: if (_logger.IsFatalEnabled) { _logger.Fatal(messageFunc()); return true; } break; default: if (_logger.IsDebugEnabled) { _logger.Debug(messageFunc()); // Log4Net doesn't have a 'Trace' level, so all Trace messages are written as 'Debug' return true; } break; } return false; } private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) { switch (logLevel) { case LogLevel.Info: if (_logger.IsDebugEnabled) { _logger.Info(messageFunc(), exception); return true; } break; case LogLevel.Warn: if (_logger.IsWarnEnabled) { _logger.Warn(messageFunc(), exception); return true; } break; case LogLevel.Error: if (_logger.IsErrorEnabled) { _logger.Error(messageFunc(), exception); return true; } break; case LogLevel.Fatal: if (_logger.IsFatalEnabled) { _logger.Fatal(messageFunc(), exception); return true; } break; default: if (_logger.IsDebugEnabled) { _logger.Debug(messageFunc(), exception); return true; } break; } return false; } private bool IsLogLevelEnable(LogLevel logLevel) { switch (logLevel) { case LogLevel.Debug: return _logger.IsDebugEnabled; case LogLevel.Info: return _logger.IsInfoEnabled; case LogLevel.Warn: return _logger.IsWarnEnabled; case LogLevel.Error: return _logger.IsErrorEnabled; case LogLevel.Fatal: return _logger.IsFatalEnabled; default: return _logger.IsDebugEnabled; } } } } public class EntLibLogProvider : ILogProvider { private const string TypeTemplate = "Microsoft.Practices.EnterpriseLibrary.Logging.{0}, Microsoft.Practices.EnterpriseLibrary.Logging"; private static bool _providerIsAvailableOverride = true; private static readonly Type LogEntryType; private static readonly Type LoggerType; private static readonly Action WriteLogEntry; private static Func ShouldLogEntry; static EntLibLogProvider() { LogEntryType = Type.GetType(string.Format(TypeTemplate, "LogEntry")); LoggerType = Type.GetType(string.Format(TypeTemplate, "Logger")); if (LogEntryType == null || LoggerType == null) { return; } WriteLogEntry = GetWriteLogEntry(); ShouldLogEntry = GetShouldLogEntry(); } public EntLibLogProvider() { if (!IsLoggerAvailable()) { throw new InvalidOperationException("Microsoft.Practices.EnterpriseLibrary.Logging.Logger not found"); } } public static bool ProviderIsAvailableOverride { get { return _providerIsAvailableOverride; } set { _providerIsAvailableOverride = value; } } public ILog GetLogger(string name) { return new EntLibLogger(name, WriteLogEntry, ShouldLogEntry); } public static bool IsLoggerAvailable() { return ProviderIsAvailableOverride && LogEntryType != null; } private static Action GetWriteLogEntry() { // new LogEntry(...) var logNameParameter = Expression.Parameter(typeof(string), "logName"); var messageParameter = Expression.Parameter(typeof(string), "message"); var severityParameter = Expression.Parameter(typeof(TraceEventType), "severity"); MemberInitExpression memberInit = GetWriteLogExpression(messageParameter, severityParameter, logNameParameter); //Logger.Write(new LogEntry(....)); MethodInfo writeLogEntryMethod = LoggerType.GetMethod("Write", new[] { LogEntryType }); var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit); return Expression.Lambda>( writeLogEntryExpression, logNameParameter, messageParameter, severityParameter).Compile(); } private static Func GetShouldLogEntry() { // new LogEntry(...) var logNameParameter = Expression.Parameter(typeof(string), "logName"); var severityParameter = Expression.Parameter(typeof(TraceEventType), "severity"); MemberInitExpression memberInit = GetWriteLogExpression(Expression.Constant("***dummy***"), severityParameter, logNameParameter); //Logger.Write(new LogEntry(....)); MethodInfo writeLogEntryMethod = LoggerType.GetMethod("ShouldLog", new[] { LogEntryType }); var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit); return Expression.Lambda>( writeLogEntryExpression, logNameParameter, severityParameter).Compile(); } private static MemberInitExpression GetWriteLogExpression(Expression message, ParameterExpression severityParameter, ParameterExpression logNameParameter) { var entryType = LogEntryType; MemberInitExpression memberInit = Expression.MemberInit(Expression.New(entryType), new MemberBinding[] { Expression.Bind(entryType.GetProperty("Message"), message), Expression.Bind(entryType.GetProperty("Severity"), severityParameter), Expression.Bind(entryType.GetProperty("TimeStamp"), Expression.Property(null, typeof (DateTime).GetProperty("UtcNow"))), Expression.Bind(entryType.GetProperty("Categories"), Expression.ListInit( Expression.New(typeof (List)), typeof (List).GetMethod("Add", new[] {typeof (string)}), logNameParameter)) }); return memberInit; } public class EntLibLogger : ILog { private readonly string _loggerName; private readonly Action _writeLog; private readonly Func _shouldLog; internal EntLibLogger(string loggerName, Action writeLog, Func shouldLog) { _loggerName = loggerName; _writeLog = writeLog; _shouldLog = shouldLog; } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception) { var severity = MapSeverity(logLevel); if (messageFunc == null) { return _shouldLog(_loggerName, severity); } if (exception != null) { return LogException(logLevel, messageFunc, exception); } _writeLog(_loggerName, messageFunc(), severity); return true; } public bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) { var severity = MapSeverity(logLevel); var message = messageFunc() + Environment.NewLine + exception; _writeLog(_loggerName, message, severity); return true; } private static TraceEventType MapSeverity(LogLevel logLevel) { switch (logLevel) { case LogLevel.Fatal: return TraceEventType.Critical; case LogLevel.Error: return TraceEventType.Error; case LogLevel.Warn: return TraceEventType.Warning; case LogLevel.Info: return TraceEventType.Information; default: return TraceEventType.Verbose; } } } } public class SerilogLogProvider : ILogProvider { private readonly Func _getLoggerByNameDelegate; private static bool _providerIsAvailableOverride = true; public SerilogLogProvider() { if (!IsLoggerAvailable()) { throw new InvalidOperationException("Serilog.Log not found"); } _getLoggerByNameDelegate = GetForContextMethodCall(); } public static bool ProviderIsAvailableOverride { get { return _providerIsAvailableOverride; } set { _providerIsAvailableOverride = value; } } public ILog GetLogger(string name) { return new SerilogLogger(_getLoggerByNameDelegate(name)); } public static bool IsLoggerAvailable() { return ProviderIsAvailableOverride && GetLogManagerType() != null; } private static Type GetLogManagerType() { return Type.GetType("Serilog.Log, Serilog"); } private static Func GetForContextMethodCall() { Type logManagerType = GetLogManagerType(); MethodInfo method = logManagerType.GetMethod("ForContext", new[] { typeof(string), typeof(object), typeof(bool) }); ParameterExpression propertyNameParam = Expression.Parameter(typeof(string), "propertyName"); ParameterExpression valueParam = Expression.Parameter(typeof(object), "value"); ParameterExpression destructureObjectsParam = Expression.Parameter(typeof(bool), "destructureObjects"); MethodCallExpression methodCall = Expression.Call(null, method, new Expression[] { propertyNameParam, valueParam, destructureObjectsParam }); var func = Expression.Lambda>(methodCall, new[] { propertyNameParam, valueParam, destructureObjectsParam }).Compile(); return name => func("Name", name, false); } public class SerilogLogger : ILog { private readonly object _logger; private static readonly object DebugLevel; private static readonly object ErrorLevel; private static readonly object FatalLevel; private static readonly object InformationLevel; private static readonly object VerboseLevel; private static readonly object WarningLevel; private static readonly Func IsEnabled; private static readonly Action Write; private static readonly Action WriteException; static SerilogLogger() { var logEventTypeType = Type.GetType("Serilog.Events.LogEventLevel, Serilog"); DebugLevel = Enum.Parse(logEventTypeType, "Debug"); ErrorLevel = Enum.Parse(logEventTypeType, "Error"); FatalLevel = Enum.Parse(logEventTypeType, "Fatal"); InformationLevel = Enum.Parse(logEventTypeType, "Information"); VerboseLevel = Enum.Parse(logEventTypeType, "Verbose"); WarningLevel = Enum.Parse(logEventTypeType, "Warning"); // Func isEnabled = (logger, level) => { return ((SeriLog.ILogger)logger).IsEnabled(level); } var loggerType = Type.GetType("Serilog.ILogger, Serilog"); MethodInfo isEnabledMethodInfo = loggerType.GetMethod("IsEnabled"); ParameterExpression instanceParam = Expression.Parameter(typeof(object)); UnaryExpression instanceCast = Expression.Convert(instanceParam, loggerType); ParameterExpression levelParam = Expression.Parameter(typeof(object)); UnaryExpression levelCast = Expression.Convert(levelParam, logEventTypeType); MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast); IsEnabled = Expression.Lambda>(isEnabledMethodCall, new[] { instanceParam, levelParam }).Compile(); // Action Write = // (logger, level, message) => { ((SeriLog.ILoggerILogger)logger).Write(level, message, new object[]); } MethodInfo writeMethodInfo = loggerType.GetMethod("Write", new[] { logEventTypeType, typeof(string), typeof(object[]) }); ParameterExpression messageParam = Expression.Parameter(typeof(string)); ConstantExpression propertyValuesParam = Expression.Constant(new object[0]); MethodCallExpression writeMethodExp = Expression.Call(instanceCast, writeMethodInfo, levelCast, messageParam, propertyValuesParam); Write = Expression.Lambda>(writeMethodExp, new[] { instanceParam, levelParam, messageParam }).Compile(); // Action WriteException = // (logger, level, exception, message) => { ((ILogger)logger).Write(level, exception, message, new object[]); } MethodInfo writeExceptionMethodInfo = loggerType.GetMethod("Write", new[] { logEventTypeType, typeof(Exception), typeof(string), typeof(object[]) }); ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); writeMethodExp = Expression.Call( instanceCast, writeExceptionMethodInfo, levelCast, exceptionParam, messageParam, propertyValuesParam); WriteException = Expression.Lambda>(writeMethodExp, new[] { instanceParam, levelParam, exceptionParam, messageParam, }).Compile(); } internal SerilogLogger(object logger) { _logger = logger; } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception) { if (messageFunc == null) { return IsEnabled(_logger, logLevel); } if (exception != null) { return LogException(logLevel, messageFunc, exception); } switch (logLevel) { case LogLevel.Debug: if (IsEnabled(_logger, DebugLevel)) { Write(_logger, DebugLevel, messageFunc()); return true; } break; case LogLevel.Info: if (IsEnabled(_logger, InformationLevel)) { Write(_logger, InformationLevel, messageFunc()); return true; } break; case LogLevel.Warn: if (IsEnabled(_logger, WarningLevel)) { Write(_logger, WarningLevel, messageFunc()); return true; } break; case LogLevel.Error: if (IsEnabled(_logger, ErrorLevel)) { Write(_logger, ErrorLevel, messageFunc()); return true; } break; case LogLevel.Fatal: if (IsEnabled(_logger, FatalLevel)) { Write(_logger, FatalLevel, messageFunc()); return true; } break; default: if (IsEnabled(_logger, VerboseLevel)) { Write(_logger, VerboseLevel, messageFunc()); return true; } break; } return false; } private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) { switch (logLevel) { case LogLevel.Debug: if (IsEnabled(_logger, DebugLevel)) { WriteException(_logger, DebugLevel, exception, messageFunc()); return true; } break; case LogLevel.Info: if (IsEnabled(_logger, InformationLevel)) { WriteException(_logger, InformationLevel, exception, messageFunc()); return true; } break; case LogLevel.Warn: if (IsEnabled(_logger, WarningLevel)) { WriteException(_logger, WarningLevel, exception, messageFunc()); return true; } break; case LogLevel.Error: if (IsEnabled(_logger, ErrorLevel)) { WriteException(_logger, ErrorLevel, exception, messageFunc()); return true; } break; case LogLevel.Fatal: if (IsEnabled(_logger, FatalLevel)) { WriteException(_logger, FatalLevel, exception, messageFunc()); return true; } break; default: if (IsEnabled(_logger, VerboseLevel)) { WriteException(_logger, VerboseLevel, exception, messageFunc()); return true; } break; } return false; } } } public class LoupeLogProvider : ILogProvider { private static bool _providerIsAvailableOverride = true; private readonly WriteDelegate _logWriteDelegate; public LoupeLogProvider() { if (!IsLoggerAvailable()) { throw new InvalidOperationException("Gibraltar.Agent.Log (Loupe) not found"); } _logWriteDelegate = GetLogWriteDelegate(); } /// /// Gets or sets a value indicating whether [provider is available override]. Used in tests. /// /// /// true if [provider is available override]; otherwise, false. /// public static bool ProviderIsAvailableOverride { get { return _providerIsAvailableOverride; } set { _providerIsAvailableOverride = value; } } public ILog GetLogger(string name) { return new LoupeLogger(name, _logWriteDelegate); } public static bool IsLoggerAvailable() { return ProviderIsAvailableOverride && GetLogManagerType() != null; } private static Type GetLogManagerType() { return Type.GetType("Gibraltar.Agent.Log, Gibraltar.Agent"); } private static WriteDelegate GetLogWriteDelegate() { Type logManagerType = GetLogManagerType(); Type logMessageSeverityType = Type.GetType("Gibraltar.Agent.LogMessageSeverity, Gibraltar.Agent"); Type logWriteModeType = Type.GetType("Gibraltar.Agent.LogWriteMode, Gibraltar.Agent"); MethodInfo method = logManagerType.GetMethod("Write", new[] { logMessageSeverityType, typeof(string), typeof(int), typeof(Exception), typeof(bool), logWriteModeType, typeof(string), typeof(string), typeof(string), typeof(string), typeof(object[]) }); var callDelegate = (WriteDelegate)Delegate.CreateDelegate(typeof(WriteDelegate), method); return callDelegate; } public class LoupeLogger : ILog { private const string LogSystem = "LibLog"; private readonly string _category; private readonly WriteDelegate _logWriteDelegate; private readonly int _skipLevel; internal LoupeLogger(string category, WriteDelegate logWriteDelegate) { _category = category; _logWriteDelegate = logWriteDelegate; _skipLevel = 1; } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception) { if (messageFunc == null) { //nothing to log.. return true; } _logWriteDelegate((int)ToLogMessageSeverity(logLevel), LogSystem, _skipLevel, exception, true, 0, null, _category, null, messageFunc.Invoke()); return true; } public TraceEventType ToLogMessageSeverity(LogLevel logLevel) { switch (logLevel) { case LogLevel.Trace: return TraceEventType.Verbose; case LogLevel.Debug: return TraceEventType.Verbose; case LogLevel.Info: return TraceEventType.Information; case LogLevel.Warn: return TraceEventType.Warning; case LogLevel.Error: return TraceEventType.Error; case LogLevel.Fatal: return TraceEventType.Critical; default: throw new ArgumentOutOfRangeException("logLevel"); } } } /// /// The form of the Loupe Log.Write method we're using /// internal delegate void WriteDelegate( int severity, string logSystem, int skipFrames, Exception exception, bool attributeToException, int writeMode, string detailsXml, string category, string caption, string description, params object[] args ); } public class ColouredConsoleLogProvider : ILogProvider { static ColouredConsoleLogProvider() { MessageFormatter = DefaultMessageFormatter; Colors = new Dictionary { { LogLevel.Fatal, ConsoleColor.Red }, { LogLevel.Error, ConsoleColor.Yellow }, { LogLevel.Warn, ConsoleColor.Magenta }, { LogLevel.Info, ConsoleColor.White }, { LogLevel.Debug, ConsoleColor.Gray }, { LogLevel.Trace, ConsoleColor.DarkGray }, }; } public ILog GetLogger(string name) { return new ColouredConsoleLogger(name); } /// /// A delegate returning a formatted log message /// /// The name of the Logger /// The Log Level /// The Log Message /// The Exception, if there is one /// A formatted Log Message string. public delegate string MessageFormatterDelegate( string loggerName, LogLevel level, object message, Exception e); public static Dictionary Colors { get; set; } public static MessageFormatterDelegate MessageFormatter { get; set; } protected static string DefaultMessageFormatter(string loggerName, LogLevel level, object message, Exception e) { var stringBuilder = new StringBuilder(); stringBuilder.Append(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss", CultureInfo.InvariantCulture)); stringBuilder.Append(" "); // Append a readable representation of the log level stringBuilder.Append(("[" + level.ToString().ToUpper() + "]").PadRight(8)); stringBuilder.Append("(" + loggerName + ") "); // Append the message stringBuilder.Append(message); // Append stack trace if there is an exception if (e != null) { stringBuilder.Append(Environment.NewLine).Append(e.GetType()); stringBuilder.Append(Environment.NewLine).Append(e.Message); stringBuilder.Append(Environment.NewLine).Append(e.StackTrace); } return stringBuilder.ToString(); } public class ColouredConsoleLogger : ILog { private readonly string _name; public ColouredConsoleLogger(string name) { _name = name; } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception) { if (messageFunc == null) { return true; } Write(logLevel, messageFunc(), exception); return true; } protected void Write(LogLevel logLevel, string message, Exception e = null) { var formattedMessage = MessageFormatter(this._name, logLevel, message, e); ConsoleColor color; if (Colors.TryGetValue(logLevel, out color)) { var originalColor = Console.ForegroundColor; try { Console.ForegroundColor = color; Console.Out.WriteLine(formattedMessage); } finally { Console.ForegroundColor = originalColor; } } else { Console.Out.WriteLine(formattedMessage); } } } } }