From 92ce3a217500a02037b284b2d33fa2e60cd1b69b Mon Sep 17 00:00:00 2001 From: Adam Ralph Date: Sat, 28 Mar 2015 18:03:49 +0100 Subject: [PATCH] replace Common.Logging with LibLog --- .../App_Packages/LibLog.3.1/LibLog.cs | 1941 +++++++++++++++++ src/ScriptCs.WebApi/ControllerTypeManager.cs | 11 +- src/ScriptCs.WebApi/ScriptCs.WebApi.csproj | 1 + src/ScriptCs.WebApi/ScriptPack.cs | 7 +- src/ScriptCs.WebApi/WebApi.cs | 5 +- src/ScriptCs.WebApi/packages.config | 1 + 6 files changed, 1948 insertions(+), 18 deletions(-) create mode 100644 src/ScriptCs.WebApi/App_Packages/LibLog.3.1/LibLog.cs diff --git a/src/ScriptCs.WebApi/App_Packages/LibLog.3.1/LibLog.cs b/src/ScriptCs.WebApi/App_Packages/LibLog.3.1/LibLog.cs new file mode 100644 index 0000000..d153390 --- /dev/null +++ b/src/ScriptCs.WebApi/App_Packages/LibLog.3.1/LibLog.cs @@ -0,0 +1,1941 @@ +//=============================================================================== +// LibLog +// +// https://github.com/damianh/LibLog +//=============================================================================== +// Copyright © 2011-2015 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. +//=============================================================================== + +// ReSharper disable PossibleNullReferenceException + +// Define LIBLOG_PORTABLE conditional compilation symbol for PCL compatibility + +#pragma warning disable 1591 + +namespace ScriptCs.WebApi.Logging +{ + using System.Collections.Generic; + using ScriptCs.WebApi.Logging.LogProviders; + using System; + using System.Diagnostics; + + /// + /// 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. + /// Optional format parameters for the message generated by the messagefunc. + /// 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, params object[] formatParameters ); + } + + /// + /// 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 DebugException(this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsDebugEnabled()) + { + logger.Log(LogLevel.Debug, message.AsFunc(), exception, formatParams); + } + } + + 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, params object[] formatParams) + { + if (logger.IsErrorEnabled()) + { + logger.Log(LogLevel.Error, message.AsFunc(), exception, formatParams); + } + } + + 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, params object[] formatParams) + { + if (logger.IsFatalEnabled()) + { + logger.Log(LogLevel.Fatal, message.AsFunc(), exception, formatParams); + } + } + + 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, params object[] formatParams) + { + if (logger.IsInfoEnabled()) + { + logger.Log(LogLevel.Info, message.AsFunc(), exception, formatParams); + } + } + + 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, params object[] formatParams) + { + if (logger.IsTraceEnabled()) + { + logger.Log(LogLevel.Trace, message.AsFunc(), exception, formatParams); + } + } + + 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, params object[] formatParams) + { + if (logger.IsWarnEnabled()) + { + logger.Log(LogLevel.Warn, message.AsFunc(), exception, formatParams); + } + } + + 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) + { + logger.Log(logLevel, message.AsFunc(), null, args); + } + + // 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 + { + /// + /// Gets the specified named logger. + /// + /// Name of the logger. + /// The logger reference. + ILog GetLogger(string name); + + /// + /// Opens a nested diagnostics context. Not supported in EntLib logging. + /// + /// The message to add to the diagnostics context. + /// A disposable that when disposed removes the message from the context. + IDisposable OpenNestedContext(string message); + + /// + /// Opens a mapped diagnostics context. Not supported in EntLib logging. + /// + /// A key. + /// A value. + /// A disposable that when disposed removes the map from the context. + IDisposable OpenMappedContext(string key, string value); + } + + /// + /// Provides a mechanism to create instances of objects. + /// + public static class LogProvider + { + private static ILogProvider _currentLogProvider; + private const string NullLogProvider = "Current Log Provider is not set. Call SetCurrentLogProvider " + + "with a non-null value first."; + + /// + /// 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)); + } + +#if !LIBLOG_PORTABLE + /// + /// 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); + } +#endif + + /// + /// 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 static IDisposable OpenNestedConext(string message) + { + if(_currentLogProvider == null) + { + throw new InvalidOperationException(NullLogProvider); + } + return _currentLogProvider.OpenNestedContext(message); + } + + public static IDisposable OpenMappedContext(string key, string value) + { + if (_currentLogProvider == null) + { + throw new InvalidOperationException(NullLogProvider); + } + return _currentLogProvider.OpenMappedContext(key, value); + } + + 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()), + new Tuple(ColouredConsoleLogProvider.IsLoggerAvailable, () => new ColouredConsoleLogProvider()), + }; + + private static ILogProvider ResolveLogProvider() + { + try + { + foreach (var providerResolver in LogProviderResolvers) + { + if (providerResolver.Item1()) + { + return providerResolver.Item2(); + } + } + } + catch (Exception ex) + { +#if LIBLOG_PORTABLE + Debug.WriteLine( +#else + Console.WriteLine( +#endif + "Exception occured resolving a log provider. Logging for this assembly {0} is disabled. {1}", + typeof(LogProvider).GetAssemblyPortable().FullName, + ex); + } + return null; + } + + internal class NoOpLogger : ILog + { + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + return false; + } + } + } + + internal class LoggerExecutionWrapper : ILog + { + private readonly ILog _logger; + internal const string FailedToGenerateLogMessage = "Failed to generate log message"; + + internal LoggerExecutionWrapper(ILog logger) + { + _logger = logger; + } + + public ILog WrappedLogger + { + get { return _logger; } + } + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters) + { + 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, formatParameters); + } + } +} + +namespace ScriptCs.WebApi.Logging.LogProviders +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq.Expressions; + using System.Reflection; + using System.Text; + using System.Text.RegularExpressions; + + internal abstract class LogProviderBase : ILogProvider + { + protected delegate IDisposable OpenNdc(string message); + protected delegate IDisposable OpenMdc(string key, string value); + + private readonly Lazy _lazyOpenNdcMethod; + private readonly Lazy _lazyOpenMdcMethod; + private static readonly IDisposable NoopDisposableInstance = new DisposableAction(); + + protected LogProviderBase() + { + _lazyOpenNdcMethod + = new Lazy(GetOpenNdcMethod); + _lazyOpenMdcMethod + = new Lazy(GetOpenMdcMethod); + } + + public abstract ILog GetLogger(string name); + + public IDisposable OpenNestedContext(string message) + { + return _lazyOpenNdcMethod.Value(message); + } + + public IDisposable OpenMappedContext(string key, string value) + { + return _lazyOpenMdcMethod.Value(key, value); + } + + protected virtual OpenNdc GetOpenNdcMethod() + { + return _ => NoopDisposableInstance; + } + + protected virtual OpenMdc GetOpenMdcMethod() + { + return (_, __) => NoopDisposableInstance; + } + } + + internal class NLogLogProvider : LogProviderBase + { + 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 override ILog GetLogger(string name) + { + return new NLogLogger(_getLoggerByNameDelegate(name)); + } + + public static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && GetLogManagerType() != null; + } + + protected override OpenNdc GetOpenNdcMethod() + { + Type ndcContextType = Type.GetType("NLog.NestedDiagnosticsContext, NLog"); + MethodInfo pushMethod = ndcContextType.GetMethodPortable("Push", new[] { typeof(string) }); + ParameterExpression messageParam = Expression.Parameter(typeof(string), "message"); + MethodCallExpression pushMethodCall = Expression.Call(null, pushMethod, messageParam); + return Expression.Lambda(pushMethodCall, messageParam).Compile(); + } + + protected override OpenMdc GetOpenMdcMethod() + { + Type mdcContextType = Type.GetType("NLog.MappedDiagnosticsContext, NLog"); + + MethodInfo setMethod = mdcContextType.GetMethodPortable("Set", new[] { typeof(string), typeof(string) }); + MethodInfo removeMethod = mdcContextType.GetMethodPortable("Remove", new[] { typeof(string) }); + ParameterExpression keyParam = Expression.Parameter(typeof(string), "key"); + ParameterExpression valueParam = Expression.Parameter(typeof(string), "value"); + + MethodCallExpression setMethodCall = Expression.Call(null, setMethod, keyParam, valueParam); + MethodCallExpression removeMethodCall = Expression.Call(null, removeMethod, keyParam); + + Action set = Expression + .Lambda>(setMethodCall, keyParam, valueParam) + .Compile(); + Action remove = Expression + .Lambda>(removeMethodCall, keyParam) + .Compile(); + + return (key, value) => + { + set(key, value); + return new DisposableAction(() => remove(key)); + }; + } + + private static Type GetLogManagerType() + { + return Type.GetType("NLog.LogManager, NLog"); + } + + private static Func GetGetLoggerMethodCall() + { + Type logManagerType = GetLogManagerType(); + MethodInfo method = logManagerType.GetMethodPortable("GetLogger", new[] { typeof(string) }); + ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); + MethodCallExpression methodCall = Expression.Call(null, method, nameParam); + return Expression.Lambda>(methodCall, 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, params object[] formatParameters) + { + if (messageFunc == null) + { + return IsLogLevelEnable(logLevel); + } + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + + 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; + } + } + } + } + + internal class Log4NetLogProvider : LogProviderBase + { + 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 override ILog GetLogger(string name) + { + return new Log4NetLogger(_getLoggerByNameDelegate(name)); + } + + internal static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && GetLogManagerType() != null; + } + + protected override OpenNdc GetOpenNdcMethod() + { + Type ndcContextType = Type.GetType("log4net.NDC, log4net"); + MethodInfo pushMethod = ndcContextType.GetMethodPortable("Push", new[] { typeof(string) }); + ParameterExpression messageParam = Expression.Parameter(typeof(string), "message"); + MethodCallExpression pushMethodCall = Expression.Call(null, pushMethod, messageParam); + return Expression.Lambda(pushMethodCall, messageParam).Compile(); + } + + protected override OpenMdc GetOpenMdcMethod() + { + Type mdcContextType = Type.GetType("log4net.MDC, log4net"); + + MethodInfo setMethod = mdcContextType.GetMethodPortable("Set", new[] { typeof(string), typeof(string) }); + MethodInfo removeMethod = mdcContextType.GetMethodPortable("Remove", new[] { typeof(string) }); + ParameterExpression keyParam = Expression.Parameter(typeof(string), "key"); + ParameterExpression valueParam = Expression.Parameter(typeof(string), "value"); + + MethodCallExpression setMethodCall = Expression.Call(null, setMethod, keyParam, valueParam); + MethodCallExpression removeMethodCall = Expression.Call(null, removeMethod, keyParam); + + Action set = Expression + .Lambda>(setMethodCall, keyParam, valueParam) + .Compile(); + Action remove = Expression + .Lambda>(removeMethodCall, keyParam) + .Compile(); + + return (key, value) => + { + set(key, value); + return new DisposableAction(() => remove(key)); + }; + } + + private static Type GetLogManagerType() + { + return Type.GetType("log4net.LogManager, log4net"); + } + + private static Func GetGetLoggerMethodCall() + { + Type logManagerType = GetLogManagerType(); + MethodInfo method = logManagerType.GetMethodPortable("GetLogger", new[] { typeof(string) }); + ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); + MethodCallExpression methodCall = Expression.Call(null, method, nameParam); + return Expression.Lambda>(methodCall, 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, params object[] formatParameters) + { + if (messageFunc == null) + { + return IsLogLevelEnable(logLevel); + } + + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + + 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; + } + } + } + } + + internal class EntLibLogProvider : LogProviderBase + { + 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 Type TraceEventTypeType; + private static readonly Action WriteLogEntry; + private static readonly Func ShouldLogEntry; + + static EntLibLogProvider() + { + LogEntryType = Type.GetType(string.Format(TypeTemplate, "LogEntry")); + LoggerType = Type.GetType(string.Format(TypeTemplate, "Logger")); + TraceEventTypeType = TraceEventTypeValues.Type; + if (LogEntryType == null + || TraceEventTypeType == 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 override ILog GetLogger(string name) + { + return new EntLibLogger(name, WriteLogEntry, ShouldLogEntry); + } + + internal static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride + && TraceEventTypeType != null + && 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(int), "severity"); + + MemberInitExpression memberInit = GetWriteLogExpression( + messageParameter, + Expression.Convert(severityParameter, TraceEventTypeType), + logNameParameter); + + //Logger.Write(new LogEntry(....)); + MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("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(int), "severity"); + + MemberInitExpression memberInit = GetWriteLogExpression( + Expression.Constant("***dummy***"), + Expression.Convert(severityParameter, TraceEventTypeType), + logNameParameter); + + //Logger.Write(new LogEntry(....)); + MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("ShouldLog", new[] { LogEntryType }); + var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit); + + return Expression.Lambda>( + writeLogEntryExpression, + logNameParameter, + severityParameter).Compile(); + } + + private static MemberInitExpression GetWriteLogExpression(Expression message, + Expression severityParameter, ParameterExpression logNameParameter) + { + var entryType = LogEntryType; + MemberInitExpression memberInit = Expression.MemberInit(Expression.New(entryType), new [] + { + Expression.Bind(entryType.GetPropertyPortable("Message"), message), + Expression.Bind(entryType.GetPropertyPortable("Severity"), severityParameter), + Expression.Bind(entryType.GetPropertyPortable("TimeStamp"), + Expression.Property(null, typeof (DateTime).GetPropertyPortable("UtcNow"))), + Expression.Bind(entryType.GetPropertyPortable("Categories"), + Expression.ListInit( + Expression.New(typeof (List)), + typeof (List).GetMethodPortable("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, params object[] formatParameters) + { + var severity = MapSeverity(logLevel); + if (messageFunc == null) + { + return _shouldLog(_loggerName, severity); + } + + + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + 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 int MapSeverity(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Fatal: + return TraceEventTypeValues.Critical; + case LogLevel.Error: + return TraceEventTypeValues.Error; + case LogLevel.Warn: + return TraceEventTypeValues.Warning; + case LogLevel.Info: + return TraceEventTypeValues.Information; + default: + return TraceEventTypeValues.Verbose; + } + } + } + } + + internal class SerilogLogProvider : LogProviderBase + { + 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 override ILog GetLogger(string name) + { + return new SerilogLogger(_getLoggerByNameDelegate(name)); + } + + internal static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && GetLogManagerType() != null; + } + + protected override OpenNdc GetOpenNdcMethod() + { + return message => GetPushProperty()("NDC", message); + } + + protected override OpenMdc GetOpenMdcMethod() + { + return (key, value) => GetPushProperty()(key, value); + } + + private static Func GetPushProperty() + { + Type ndcContextType = Type.GetType("Serilog.Context.LogContext, Serilog.FullNetFx"); + MethodInfo pushPropertyMethod = ndcContextType.GetMethodPortable( + "PushProperty", + new[] + { + typeof(string), + typeof(object), + typeof(bool) + }); + ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); + ParameterExpression valueParam = Expression.Parameter(typeof(object), "value"); + ParameterExpression destructureObjectParam = Expression.Parameter(typeof(bool), "destructureObjects"); + MethodCallExpression pushPropertyMethodCall = Expression + .Call(null, pushPropertyMethod, nameParam, valueParam, destructureObjectParam); + var pushProperty = Expression + .Lambda>( + pushPropertyMethodCall, + nameParam, + valueParam, + destructureObjectParam) + .Compile(); + + return (key, value) => pushProperty(key, value, false); + } + + private static Type GetLogManagerType() + { + return Type.GetType("Serilog.Log, Serilog"); + } + + private static Func GetForContextMethodCall() + { + Type logManagerType = GetLogManagerType(); + MethodInfo method = logManagerType.GetMethodPortable("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, + 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", false); + ErrorLevel = Enum.Parse(logEventTypeType, "Error", false); + FatalLevel = Enum.Parse(logEventTypeType, "Fatal", false); + InformationLevel = Enum.Parse(logEventTypeType, "Information", false); + VerboseLevel = Enum.Parse(logEventTypeType, "Verbose", false); + WarningLevel = Enum.Parse(logEventTypeType, "Warning", false); + + // Func isEnabled = (logger, level) => { return ((SeriLog.ILogger)logger).IsEnabled(level); } + var loggerType = Type.GetType("Serilog.ILogger, Serilog"); + var logEventLevelType = Type.GetType("Serilog.Events.LogEventLevel, Serilog"); + MethodInfo isEnabledMethodInfo = loggerType.GetMethodPortable("IsEnabled", logEventLevelType); + 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, instanceParam, levelParam).Compile(); + + // Action Write = + // (logger, level, message, params) => { ((SeriLog.ILoggerILogger)logger).Write(level, message, params); } + MethodInfo writeMethodInfo = loggerType.GetMethodPortable("Write", new[] { logEventTypeType, typeof(string), typeof(object[]) }); + ParameterExpression messageParam = Expression.Parameter(typeof(string)); + ParameterExpression propertyValuesParam = Expression.Parameter(typeof(object[])); + MethodCallExpression writeMethodExp = Expression.Call(instanceCast, writeMethodInfo, levelCast, messageParam, propertyValuesParam); + var expression = Expression.Lambda>( + writeMethodExp, + instanceParam, + levelParam, + messageParam, + propertyValuesParam); + Write = expression.Compile(); + + // Action WriteException = + // (logger, level, exception, message) => { ((ILogger)logger).Write(level, exception, message, new object[]); } + MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("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, + instanceParam, + levelParam, + exceptionParam, + messageParam, + propertyValuesParam).Compile(); + } + + internal SerilogLogger(object logger) + { + _logger = logger; + } + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + if (messageFunc == null) + { + return IsEnabled(_logger, logLevel); + } + if (exception != null) + { + return LogException(logLevel, messageFunc, exception, formatParameters); + } + + switch (logLevel) + { + case LogLevel.Debug: + if (IsEnabled(_logger, DebugLevel)) + { + Write(_logger, DebugLevel, messageFunc(), formatParameters); + return true; + } + break; + case LogLevel.Info: + if (IsEnabled(_logger, InformationLevel)) + { + Write(_logger, InformationLevel, messageFunc(), formatParameters); + return true; + } + break; + case LogLevel.Warn: + if (IsEnabled(_logger, WarningLevel)) + { + Write(_logger, WarningLevel, messageFunc(), formatParameters); + return true; + } + break; + case LogLevel.Error: + if (IsEnabled(_logger, ErrorLevel)) + { + Write(_logger, ErrorLevel, messageFunc(), formatParameters); + return true; + } + break; + case LogLevel.Fatal: + if (IsEnabled(_logger, FatalLevel)) + { + Write(_logger, FatalLevel, messageFunc(), formatParameters); + return true; + } + break; + default: + if (IsEnabled(_logger, VerboseLevel)) + { + Write(_logger, VerboseLevel, messageFunc(), formatParameters); + return true; + } + break; + } + return false; + } + + private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception, object[] formatParams) + { + switch (logLevel) + { + case LogLevel.Debug: + if (IsEnabled(_logger, DebugLevel)) + { + WriteException(_logger, DebugLevel, exception, messageFunc(), formatParams); + return true; + } + break; + case LogLevel.Info: + if (IsEnabled(_logger, InformationLevel)) + { + WriteException(_logger, InformationLevel, exception, messageFunc(), formatParams); + return true; + } + break; + case LogLevel.Warn: + if (IsEnabled(_logger, WarningLevel)) + { + WriteException(_logger, WarningLevel, exception, messageFunc(), formatParams); + return true; + } + break; + case LogLevel.Error: + if (IsEnabled(_logger, ErrorLevel)) + { + WriteException(_logger, ErrorLevel, exception, messageFunc(), formatParams); + return true; + } + break; + case LogLevel.Fatal: + if (IsEnabled(_logger, FatalLevel)) + { + WriteException(_logger, FatalLevel, exception, messageFunc(), formatParams); + return true; + } + break; + default: + if (IsEnabled(_logger, VerboseLevel)) + { + WriteException(_logger, VerboseLevel, exception, messageFunc(), formatParams); + return true; + } + break; + } + return false; + } + } + } + + internal class LoupeLogProvider : LogProviderBase + { + /// + /// 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 + ); + + 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 override 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.GetMethodPortable( + "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)method.CreateDelegate(typeof(WriteDelegate)); + 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, params object[] formatParameters) + { + if (messageFunc == null) + { + //nothing to log.. + return true; + } + + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + + _logWriteDelegate(ToLogMessageSeverity(logLevel), LogSystem, _skipLevel, exception, true, 0, null, + _category, null, messageFunc.Invoke()); + + return true; + } + + private int ToLogMessageSeverity(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Trace: + return TraceEventTypeValues.Verbose; + case LogLevel.Debug: + return TraceEventTypeValues.Verbose; + case LogLevel.Info: + return TraceEventTypeValues.Information; + case LogLevel.Warn: + return TraceEventTypeValues.Warning; + case LogLevel.Error: + return TraceEventTypeValues.Error; + case LogLevel.Fatal: + return TraceEventTypeValues.Critical; + default: + throw new ArgumentOutOfRangeException("logLevel"); + } + } + } + } + + internal static class TraceEventTypeValues + { + internal static readonly Type Type; + internal static readonly int Verbose; + internal static readonly int Information; + internal static readonly int Warning; + internal static readonly int Error; + internal static readonly int Critical; + + static TraceEventTypeValues() + { + var assembly = typeof(Uri).GetAssemblyPortable(); // This is to get to the System.dll assembly in a PCL compatible way. + if (assembly == null) + { + return; + } + Type = assembly.GetType("System.Diagnostics.TraceEventType"); + if (Type == null) return; + Verbose = (int)Enum.Parse(Type, "Verbose", false); + Information = (int)Enum.Parse(Type, "Information", false); + Warning = (int)Enum.Parse(Type, "Warning", false); + Error = (int)Enum.Parse(Type, "Error", false); + Critical = (int)Enum.Parse(Type, "Critical", false); + } + } + + internal class ColouredConsoleLogProvider : LogProviderBase + { + private static readonly Type ConsoleType; + private static readonly Type ConsoleColorType; + private static readonly Action ConsoleWriteLine; + private static readonly Func GetConsoleForeground; + private static readonly Action SetConsoleForeground; + private static bool _providerIsAvailableOverride = true; + private static readonly IDictionary Colors; + + static ColouredConsoleLogProvider() + { + ConsoleType = Type.GetType("System.Console"); + ConsoleColorType = ConsoleColorValues.Type; + + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("System.Console or System.ConsoleColor type not found"); + } + + MessageFormatter = DefaultMessageFormatter; + Colors = new Dictionary + { + {LogLevel.Fatal, ConsoleColorValues.Red}, + {LogLevel.Error, ConsoleColorValues.Yellow}, + {LogLevel.Warn, ConsoleColorValues.Magenta}, + {LogLevel.Info, ConsoleColorValues.White}, + {LogLevel.Debug, ConsoleColorValues.Gray}, + {LogLevel.Trace, ConsoleColorValues.DarkGray}, + }; + ConsoleWriteLine = GetConsoleWrite(); + GetConsoleForeground = GetGetConsoleForeground(); + SetConsoleForeground = GetSetConsoleForeground(); + } + + internal static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && ConsoleType != null && ConsoleColorType != null; + } + + public override ILog GetLogger(string name) + { + return new ColouredConsoleLogger(name, ConsoleWriteLine, GetConsoleForeground, SetConsoleForeground); + } + + /// + /// 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. + internal delegate string MessageFormatterDelegate( + string loggerName, + LogLevel level, + object message, + Exception e); + + internal static MessageFormatterDelegate MessageFormatter { get; set; } + + public static bool ProviderIsAvailableOverride + { + get { return _providerIsAvailableOverride; } + set { _providerIsAvailableOverride = value; } + } + + 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(); + } + + private static Action GetConsoleWrite() + { + var messageParameter = Expression.Parameter(typeof(string), "message"); + + MethodInfo writeMethod = ConsoleType.GetMethodPortable("WriteLine", new[] { typeof(string) }); + var writeExpression = Expression.Call(writeMethod, messageParameter); + + return Expression.Lambda>( + writeExpression, messageParameter).Compile(); + } + + private static Func GetGetConsoleForeground() + { + MethodInfo getForeground = ConsoleType.GetPropertyPortable("ForegroundColor").GetGetMethod(); + var getForegroundExpression = Expression.Convert(Expression.Call(getForeground), typeof(int)); + + return Expression.Lambda>(getForegroundExpression).Compile(); + } + + private static Action GetSetConsoleForeground() + { + var colorParameter = Expression.Parameter(typeof(int), "color"); + + MethodInfo setForeground = ConsoleType.GetPropertyPortable("ForegroundColor").GetSetMethod(); + var setForegroundExpression = Expression.Call(setForeground, + Expression.Convert(colorParameter, ConsoleColorType)); + + return Expression.Lambda>( + setForegroundExpression, colorParameter).Compile(); + } + + public class ColouredConsoleLogger : ILog + { + private readonly string _name; + private readonly Action _write; + private readonly Func _getForeground; + private readonly Action _setForeground; + + public ColouredConsoleLogger(string name, Action write, + Func getForeground, Action setForeground) + { + _name = name; + _write = write; + _getForeground = getForeground; + _setForeground = setForeground; + } + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, + params object[] formatParameters) + { + if (messageFunc == null) + { + return true; + } + + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + + Write(logLevel, messageFunc(), exception); + return true; + } + + protected void Write(LogLevel logLevel, string message, Exception e = null) + { + var formattedMessage = MessageFormatter(this._name, logLevel, message, e); + int color; + + if (Colors.TryGetValue(logLevel, out color)) + { + var originalColor = _getForeground(); + try + { + _setForeground(color); + _write(formattedMessage); + } + finally + { + _setForeground(originalColor); + } + } + else + { + _write(formattedMessage); + } + } + } + + private static class ConsoleColorValues + { + internal static readonly Type Type; + internal static readonly int Red; + internal static readonly int Yellow; + internal static readonly int Magenta; + internal static readonly int White; + internal static readonly int Gray; + internal static readonly int DarkGray; + + static ConsoleColorValues() + { + Type = Type.GetType("System.ConsoleColor"); + if (Type == null) return; + Red = (int)Enum.Parse(Type, "Red", false); + Yellow = (int)Enum.Parse(Type, "Yellow", false); + Magenta = (int)Enum.Parse(Type, "Magenta", false); + White = (int)Enum.Parse(Type, "White", false); + Gray = (int)Enum.Parse(Type, "Gray", false); + DarkGray = (int)Enum.Parse(Type, "DarkGray", false); + } + } + } + + internal static class LogMessageFormatter + { + private static readonly Regex Pattern = new Regex(@"\{\w{1,}\}"); + + /// + /// Some logging frameworks support structured logging, such as serilog. This will allow you to add names to structured data in a format string: + /// For example: Log("Log message to {user}", user). This only works with serilog, but as the user of LibLog, you don't know if serilog is actually + /// used. So, this class simulates that. it will replace any text in {curlybraces} with an index number. + /// + /// "Log {message} to {user}" would turn into => "Log {0} to {1}". Then the format parameters are handled using regular .net string.Format. + /// + /// The message builder. + /// The format parameters. + /// + public static Func SimulateStructuredLogging(Func messageBuilder, object[] formatParameters) + { + if(formatParameters == null) + { + return messageBuilder; + } + + return () => + { + string targetMessage = messageBuilder(); + int argumentIndex = 0; + foreach (Match match in Pattern.Matches(targetMessage)) + { + int notUsed; + if (!int.TryParse(match.Value.Substring(1, match.Value.Length -2), out notUsed)) + { + targetMessage = ReplaceFirst(targetMessage, match.Value, + "{" + argumentIndex++ + "}"); + } + } + try + { + return String.Format(CultureInfo.InvariantCulture, targetMessage, formatParameters); + } + catch (FormatException ex) + { + throw new FormatException("The input string '" + targetMessage + "' could not be formatted using string.Format", ex); + } + }; + } + + private static string ReplaceFirst(string text, string search, string replace) + { + int pos = text.IndexOf(search, StringComparison.Ordinal); + if (pos < 0) + { + return text; + } + return text.Substring(0, pos) + replace + text.Substring(pos + search.Length); + } + } + + internal static class TypeExtensions + { + internal static MethodInfo GetMethodPortable(this Type type, string name) + { +#if LIBLOG_PORTABLE + return type.GetRuntimeMethod(name, new Type[]{}); +#else + return type.GetMethod(name); +#endif + } + + internal static MethodInfo GetMethodPortable(this Type type, string name, params Type[] types) + { +#if LIBLOG_PORTABLE + return type.GetRuntimeMethod(name, types); +#else + return type.GetMethod(name, types); +#endif + } + + internal static PropertyInfo GetPropertyPortable(this Type type, string name) + { +#if LIBLOG_PORTABLE + return type.GetRuntimeProperty(name); +#else + return type.GetProperty(name); +#endif + } + +#if LIBLOG_PORTABLE + internal static MethodInfo GetGetMethod(this PropertyInfo propertyInfo) + { + return propertyInfo.GetMethod; + } + + internal static MethodInfo GetSetMethod(this PropertyInfo propertyInfo) + { + return propertyInfo.SetMethod; + } +#endif + +#if !LIBLOG_PORTABLE + internal static object CreateDelegate(this MethodInfo methodInfo, Type delegateType) + { + return Delegate.CreateDelegate(delegateType, methodInfo); + } +#endif + + internal static Assembly GetAssemblyPortable(this Type type) + { +#if LIBLOG_PORTABLE + return type.GetTypeInfo().Assembly; +#else + return type.Assembly; +#endif + } + } + + internal class DisposableAction : IDisposable + { + private readonly Action _onDispose; + + public DisposableAction(Action onDispose = null) + { + _onDispose = onDispose; + } + + public void Dispose() + { + if(_onDispose != null) + { + _onDispose(); + } + } + } +} diff --git a/src/ScriptCs.WebApi/ControllerTypeManager.cs b/src/ScriptCs.WebApi/ControllerTypeManager.cs index 14a3451..e4e0700 100644 --- a/src/ScriptCs.WebApi/ControllerTypeManager.cs +++ b/src/ScriptCs.WebApi/ControllerTypeManager.cs @@ -5,14 +5,14 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; -using Common.Logging; +using ScriptCs.WebApi.Logging; namespace ScriptCs.WebApi { [Export(typeof(IControllerTypeManager))] public class ControllerTypeManager : IControllerTypeManager { - private readonly ILog _logger; + private static readonly ILog _logger = LogProvider.GetCurrentClassLogger(); private const string RoslynAssemblyNameCharacter = "ℛ"; private static readonly string[] IgnoredAssemblyPrefixes = new[] @@ -31,13 +31,6 @@ public class ControllerTypeManager : IControllerTypeManager "ServiceStack.", }; - - [ImportingConstructor] - public ControllerTypeManager(ILog logger) - { - _logger = logger; - } - public IEnumerable GetLoadedTypes() { var types = new List(); diff --git a/src/ScriptCs.WebApi/ScriptCs.WebApi.csproj b/src/ScriptCs.WebApi/ScriptCs.WebApi.csproj index 388c08b..6568c5a 100644 --- a/src/ScriptCs.WebApi/ScriptCs.WebApi.csproj +++ b/src/ScriptCs.WebApi/ScriptCs.WebApi.csproj @@ -78,6 +78,7 @@ + diff --git a/src/ScriptCs.WebApi/ScriptPack.cs b/src/ScriptCs.WebApi/ScriptPack.cs index 8141a2a..a84c5b6 100644 --- a/src/ScriptCs.WebApi/ScriptPack.cs +++ b/src/ScriptCs.WebApi/ScriptPack.cs @@ -4,7 +4,6 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; -using Common.Logging; using ScriptCs; using ScriptCs.Contracts; @@ -12,19 +11,17 @@ namespace ScriptCs.WebApi { public class ScriptPack : IScriptPack { - private readonly ILog _logger; private readonly IControllerTypeManager _typeManager; [ImportingConstructor] - public ScriptPack(ILog logger, IControllerTypeManager typeManager) + public ScriptPack(IControllerTypeManager typeManager) { - _logger = logger; _typeManager = typeManager; } IScriptPackContext IScriptPack.GetContext() { - return new WebApi(_logger, _typeManager); + return new WebApi(_typeManager); } void IScriptPack.Initialize(IScriptPackSession session) diff --git a/src/ScriptCs.WebApi/WebApi.cs b/src/ScriptCs.WebApi/WebApi.cs index 391abfa..519b11f 100644 --- a/src/ScriptCs.WebApi/WebApi.cs +++ b/src/ScriptCs.WebApi/WebApi.cs @@ -8,7 +8,6 @@ using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Http.Routing; -using Common.Logging; using Microsoft.Owin.Builder; using Microsoft.SqlServer.Server; using Owin; @@ -25,7 +24,6 @@ namespace ScriptCs.WebApi /// public class WebApi : IScriptPackContext { - private readonly ILog _logger; private readonly IControllerTypeManager _typeManager; private Action _startupAction; private HttpConfiguration _config; @@ -33,9 +31,8 @@ public class WebApi : IScriptPackContext private bool _useJsonOnly; private MediaTypeFormatter _theFormatter; - public WebApi(ILog logger, IControllerTypeManager typeManager) + public WebApi(IControllerTypeManager typeManager) { - _logger = logger; _typeManager = typeManager; } diff --git a/src/ScriptCs.WebApi/packages.config b/src/ScriptCs.WebApi/packages.config index 6b38a31..37df4a4 100644 --- a/src/ScriptCs.WebApi/packages.config +++ b/src/ScriptCs.WebApi/packages.config @@ -1,5 +1,6 @@  +