From 262480bfa7b1cff7385d28b73968ce9b5e063405 Mon Sep 17 00:00:00 2001 From: Elad Zelingher Date: Sat, 29 Oct 2016 01:18:06 +0300 Subject: [PATCH] Updating LibLog --- .../App_Packages/LibLog.4.2/LibLog.cs | 394 +++++++++++++++--- src/net45/WampSharp/packages.config | 4 +- 2 files changed, 331 insertions(+), 67 deletions(-) diff --git a/src/net45/WampSharp/App_Packages/LibLog.4.2/LibLog.cs b/src/net45/WampSharp/App_Packages/LibLog.4.2/LibLog.cs index baad2897a..c7d11095b 100644 --- a/src/net45/WampSharp/App_Packages/LibLog.4.2/LibLog.cs +++ b/src/net45/WampSharp/App_Packages/LibLog.4.2/LibLog.cs @@ -422,11 +422,6 @@ interface ILogProvider static class LogProvider { #if !LIBLOG_PROVIDERS_ONLY - /// - /// The disable logging environment variable. If the environment variable is set to 'true', then logging - /// will be disabled. - /// - public const string DisableLoggingEnvironmentVariable = "WampSharp_LIBLOG_DISABLE"; private const string NullLogProvider = "Current Log Provider is not set. Call SetCurrentLogProvider " + "with a non-null value first."; private static dynamic s_currentLogProvider; @@ -517,15 +512,17 @@ static ILog GetCurrentClassLogger() /// Gets a logger for the specified type. /// /// The type whose name will be used for the logger. + /// If the type is null then this name will be used as the log name instead /// An instance of #if LIBLOG_PUBLIC public #else internal #endif - static ILog GetLogger(Type type) + static ILog GetLogger(Type type, string fallbackTypeName = "System.Object") { - return GetLogger(type.FullName); + // If the type passed in is null then fallback to the type name specified + return GetLogger(type != null ? type.FullName : fallbackTypeName); } /// @@ -541,7 +538,7 @@ static ILog GetLogger(Type type) static ILog GetLogger(string name) { ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); - return logProvider == null + return logProvider == null ? NoOpLogger.Instance : (ILog)new LoggerExecutionWrapper(logProvider.GetLogger(name), () => IsDisabled); } @@ -693,15 +690,6 @@ public bool Log(LogLevel logLevel, Func messageFunc, Exception exception { return false; } -#if !LIBLOG_PORTABLE - var envVar = Environment.GetEnvironmentVariable(LogProvider.DisableLoggingEnvironmentVariable); - - if (envVar != null && envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) - { - return false; - } -#endif - if (messageFunc == null) { return _logger(logLevel, null); @@ -757,7 +745,7 @@ internal abstract class LogProviderBase : ILogProvider protected LogProviderBase() { - _lazyOpenNdcMethod + _lazyOpenNdcMethod = new Lazy(GetOpenNdcMethod); _lazyOpenMdcMethod = new Lazy(GetOpenMdcMethod); @@ -871,6 +859,55 @@ internal class NLogLogger { private readonly dynamic _logger; + private static Func _logEventInfoFact; + + private static readonly object _levelTrace; + private static readonly object _levelDebug; + private static readonly object _levelInfo; + private static readonly object _levelWarn; + private static readonly object _levelError; + private static readonly object _levelFatal; + + static NLogLogger() + { + try + { + var logEventLevelType = Type.GetType("NLog.LogLevel, NLog"); + if (logEventLevelType == null) + { + throw new InvalidOperationException("Type NLog.LogLevel was not found."); + } + + var levelFields = logEventLevelType.GetFieldsPortable().ToList(); + _levelTrace = levelFields.First(x => x.Name == "Trace").GetValue(null); + _levelDebug = levelFields.First(x => x.Name == "Debug").GetValue(null); + _levelInfo = levelFields.First(x => x.Name == "Info").GetValue(null); + _levelWarn = levelFields.First(x => x.Name == "Warn").GetValue(null); + _levelError = levelFields.First(x => x.Name == "Error").GetValue(null); + _levelFatal = levelFields.First(x => x.Name == "Fatal").GetValue(null); + + var logEventInfoType = Type.GetType("NLog.LogEventInfo, NLog"); + if (logEventInfoType == null) + { + throw new InvalidOperationException("Type NLog.LogEventInfo was not found."); + } + MethodInfo createLogEventInfoMethodInfo = logEventInfoType.GetMethodPortable("Create", + logEventLevelType, typeof(string), typeof(Exception), typeof(IFormatProvider), typeof(string), typeof(object[])); + ParameterExpression loggerNameParam = Expression.Parameter(typeof(string)); + ParameterExpression levelParam = Expression.Parameter(typeof(object)); + ParameterExpression messageParam = Expression.Parameter(typeof(string)); + ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); + UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); + MethodCallExpression createLogEventInfoMethodCall = Expression.Call(null, + createLogEventInfoMethodInfo, + levelCast, loggerNameParam, exceptionParam, + Expression.Constant(null, typeof(IFormatProvider)), messageParam, Expression.Constant(null, typeof(object[]))); + _logEventInfoFact = Expression.Lambda>(createLogEventInfoMethodCall, + loggerNameParam, levelParam, messageParam, exceptionParam).Compile(); + } + catch { } + } + internal NLogLogger(dynamic logger) { _logger = logger; @@ -885,6 +922,43 @@ public bool Log(LogLevel logLevel, Func messageFunc, Exception exception } messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + if (_logEventInfoFact != null) + { + if (IsLogLevelEnable(logLevel)) + { + var nlogLevel = this.TranslateLevel(logLevel); + Type s_callerStackBoundaryType; +#if !LIBLOG_PORTABLE + StackTrace stack = new StackTrace(); + Type thisType = GetType(); + Type knownType0 = typeof(LoggerExecutionWrapper); + Type knownType1 = typeof(LogExtensions); + //Maybe inline, so we may can't found any LibLog classes in stack + s_callerStackBoundaryType = null; + for (var i = 0; i < stack.FrameCount; i++) + { + var declaringType = stack.GetFrame(i).GetMethod().DeclaringType; + if (!IsInTypeHierarchy(thisType, declaringType) && + !IsInTypeHierarchy(knownType0, declaringType) && + !IsInTypeHierarchy(knownType1, declaringType)) + { + if (i > 1) + s_callerStackBoundaryType = stack.GetFrame(i - 1).GetMethod().DeclaringType; + break; + } + } +#else + s_callerStackBoundaryType = null; +#endif + if (s_callerStackBoundaryType != null) + _logger.Log(s_callerStackBoundaryType, _logEventInfoFact(_logger.Name, nlogLevel, messageFunc(), exception)); + else + _logger.Log(_logEventInfoFact(_logger.Name, nlogLevel, messageFunc(), exception)); + return true; + } + return false; + } + if(exception != null) { return LogException(logLevel, messageFunc, exception); @@ -937,6 +1011,19 @@ public bool Log(LogLevel logLevel, Func messageFunc, Exception exception return false; } + private static bool IsInTypeHierarchy(Type currentType, Type checkType) + { + while (currentType != null && currentType != typeof(object)) + { + if (currentType == checkType) + { + return true; + } + currentType = currentType.GetBaseTypePortable(); + } + return false; + } + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) { @@ -1006,6 +1093,27 @@ private bool IsLogLevelEnable(LogLevel logLevel) return _logger.IsTraceEnabled; } } + + private object TranslateLevel(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Trace: + return _levelTrace; + case LogLevel.Debug: + return _levelDebug; + case LogLevel.Info: + return _levelInfo; + case LogLevel.Warn: + return _levelWarn; + case LogLevel.Error: + return _levelError; + case LogLevel.Fatal: + return _levelFatal; + default: + throw new ArgumentOutOfRangeException("logLevel", logLevel, null); + } + } } } @@ -1129,7 +1237,9 @@ internal class Log4NetLogger private readonly object _levelError; private readonly object _levelFatal; private readonly Func _isEnabledForDelegate; - private readonly Action _logDelegate; + private readonly Action _logDelegate; + private readonly Func _createLoggingEvent; + private Action _loggingEventPropertySetter; [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "ILogger")] internal Log4NetLogger(dynamic logger) @@ -1155,38 +1265,123 @@ internal Log4NetLogger(dynamic logger) { throw new InvalidOperationException("Type log4net.Core.ILogger, was not found."); } - MethodInfo isEnabledMethodInfo = loggerType.GetMethodPortable("IsEnabledFor", logEventLevelType); ParameterExpression instanceParam = Expression.Parameter(typeof(object)); UnaryExpression instanceCast = Expression.Convert(instanceParam, loggerType); - ParameterExpression callerStackBoundaryDeclaringTypeParam = Expression.Parameter(typeof(Type)); ParameterExpression levelParam = Expression.Parameter(typeof(object)); - ParameterExpression messageParam = Expression.Parameter(typeof(string)); UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); - MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast); - _isEnabledForDelegate = Expression.Lambda>(isEnabledMethodCall, instanceParam, levelParam).Compile(); + _isEnabledForDelegate = GetIsEnabledFor(loggerType, logEventLevelType, instanceCast, levelCast, instanceParam, levelParam); + + Type loggingEventType = Type.GetType("log4net.Core.LoggingEvent, log4net"); + + _createLoggingEvent = GetCreateLoggingEvent(instanceParam, instanceCast, levelParam, levelCast, loggingEventType); + + _logDelegate = GetLogDelegate(loggerType, loggingEventType, instanceCast, instanceParam); - // Action Log = - // (logger, callerStackBoundaryDeclaringType, level, message, exception) => { ((ILogger)logger).Write(callerStackBoundaryDeclaringType, level, message, exception); } + _loggingEventPropertySetter = GetLoggingEventPropertySetter(loggingEventType); + } + + private static Action GetLogDelegate(Type loggerType, Type loggingEventType, UnaryExpression instanceCast, + ParameterExpression instanceParam) + { + //Action Log = + //(logger, callerStackBoundaryDeclaringType, level, message, exception) => { ((ILogger)logger).Log(new LoggingEvent(callerStackBoundaryDeclaringType, logger.Repository, logger.Name, level, message, exception)); } MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("Log", - typeof(Type), - logEventLevelType, - typeof(string), - typeof(Exception)); - ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); + loggingEventType); + + ParameterExpression loggingEventParameter = + Expression.Parameter(typeof(object), "loggingEvent"); + + UnaryExpression loggingEventCasted = + Expression.Convert(loggingEventParameter, loggingEventType); + var writeMethodExp = Expression.Call( instanceCast, writeExceptionMethodInfo, - callerStackBoundaryDeclaringTypeParam, - levelCast, - messageParam, - exceptionParam); - _logDelegate = Expression.Lambda>( - writeMethodExp, - instanceParam, - callerStackBoundaryDeclaringTypeParam, - levelParam, - messageParam, - exceptionParam).Compile(); + loggingEventCasted); + + var logDelegate = Expression.Lambda>( + writeMethodExp, + instanceParam, + loggingEventParameter).Compile(); + + return logDelegate; + } + + private static Func GetCreateLoggingEvent(ParameterExpression instanceParam, UnaryExpression instanceCast, ParameterExpression levelParam, UnaryExpression levelCast, Type loggingEventType) + { + ParameterExpression callerStackBoundaryDeclaringTypeParam = Expression.Parameter(typeof(Type)); + ParameterExpression messageParam = Expression.Parameter(typeof(string)); + ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); + + PropertyInfo repositoryProperty = loggingEventType.GetPropertyPortable("Repository"); + PropertyInfo levelProperty = loggingEventType.GetPropertyPortable("Level"); + + ConstructorInfo loggingEventConstructor = + loggingEventType.GetConstructorPortable(typeof(Type), repositoryProperty.PropertyType, typeof(string), levelProperty.PropertyType, typeof(object), typeof(Exception)); + + //Func Log = + //(logger, callerStackBoundaryDeclaringType, level, message, exception) => new LoggingEvent(callerStackBoundaryDeclaringType, ((ILogger)logger).Repository, ((ILogger)logger).Name, (Level)level, message, exception); } + NewExpression newLoggingEventExpression = + Expression.New(loggingEventConstructor, + callerStackBoundaryDeclaringTypeParam, + Expression.Property(instanceCast, "Repository"), + Expression.Property(instanceCast, "Name"), + levelCast, + messageParam, + exceptionParam); + + var createLoggingEvent = + Expression.Lambda>( + newLoggingEventExpression, + instanceParam, + callerStackBoundaryDeclaringTypeParam, + levelParam, + messageParam, + exceptionParam) + .Compile(); + + return createLoggingEvent; + } + + private static Func GetIsEnabledFor(Type loggerType, Type logEventLevelType, + UnaryExpression instanceCast, + UnaryExpression levelCast, + ParameterExpression instanceParam, + ParameterExpression levelParam) + { + MethodInfo isEnabledMethodInfo = loggerType.GetMethodPortable("IsEnabledFor", logEventLevelType); + MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast); + + Func result = + Expression.Lambda>(isEnabledMethodCall, instanceParam, levelParam) + .Compile(); + + return result; + } + + private static Action GetLoggingEventPropertySetter(Type loggingEventType) + { + ParameterExpression loggingEventParameter = Expression.Parameter(typeof(object), "loggingEvent"); + ParameterExpression keyParameter = Expression.Parameter(typeof(string), "key"); + ParameterExpression valueParameter = Expression.Parameter(typeof(object), "value"); + + PropertyInfo propertiesProperty = loggingEventType.GetPropertyPortable("Properties"); + PropertyInfo item = propertiesProperty.PropertyType.GetPropertyPortable("Item"); + + // ((LoggingEvent)loggingEvent).Properties[key] = value; + var body = + Expression.Assign( + Expression.Property( + Expression.Property(Expression.Convert(loggingEventParameter, loggingEventType), + propertiesProperty), item, keyParameter), valueParameter); + + Action result = + Expression.Lambda> + (body, loggingEventParameter, keyParameter, + valueParameter) + .Compile(); + + return result; } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) @@ -1201,7 +1396,14 @@ public bool Log(LogLevel logLevel, Func messageFunc, Exception exception return false; } - messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + string message = messageFunc(); + + IEnumerable patternMatches; + + string formattedMessage = + LogMessageFormatter.FormatStructuredMessage(message, + formatParameters, + out patternMatches); // determine correct caller - this might change due to jit optimizations with method inlining if (s_callerStackBoundaryType == null) @@ -1227,10 +1429,28 @@ public bool Log(LogLevel logLevel, Func messageFunc, Exception exception } var translatedLevel = TranslateLevel(logLevel); - _logDelegate(_logger, s_callerStackBoundaryType, translatedLevel, messageFunc(), exception); + + object loggingEvent = _createLoggingEvent(_logger, s_callerStackBoundaryType, translatedLevel, formattedMessage, exception); + + PopulateProperties(loggingEvent, patternMatches, formatParameters); + + _logDelegate(_logger, loggingEvent); + return true; } + private void PopulateProperties(object loggingEvent, IEnumerable patternMatches, object[] formatParameters) + { + IEnumerable> keyToValue = + patternMatches.Zip(formatParameters, + (key, value) => new KeyValuePair(key, value)); + + foreach (KeyValuePair keyValuePair in keyToValue) + { + _loggingEventPropertySetter(loggingEvent, keyValuePair.Key, keyValuePair.Value); + } + } + private static bool IsInTypeHierarchy(Type currentType, Type checkType) { while (currentType != null && currentType != typeof(object)) @@ -1373,7 +1593,7 @@ private static MemberInitExpression GetWriteLogExpression(Expression message, Expression severityParameter, ParameterExpression logNameParameter) { var entryType = LogEntryType; - MemberInitExpression memberInit = Expression.MemberInit(Expression.New(entryType), + MemberInitExpression memberInit = Expression.MemberInit(Expression.New(entryType), Expression.Bind(entryType.GetPropertyPortable("Message"), message), Expression.Bind(entryType.GetPropertyPortable("Severity"), severityParameter), Expression.Bind( @@ -1489,12 +1709,15 @@ protected override OpenMdc GetOpenMdcMethod() private static Func GetPushProperty() { - Type ndcContextType = Type.GetType("Serilog.Context.LogContext, Serilog.FullNetFx"); + Type ndcContextType = Type.GetType("Serilog.Context.LogContext, Serilog") ?? + Type.GetType("Serilog.Context.LogContext, Serilog.FullNetFx"); + MethodInfo pushPropertyMethod = ndcContextType.GetMethodPortable( "PushProperty", 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"); @@ -1849,7 +2072,12 @@ static TraceEventTypeValues() internal static class LogMessageFormatter { - private static readonly Regex Pattern = new Regex(@"\{@?\w{1,}\}"); + //private static readonly Regex Pattern = new Regex(@"\{@?\w{1,}\}"); +#if LIBLOG_PORTABLE + private static readonly Regex Pattern = new Regex(@"(?[^\d{][^ }]*)}"); +#else + private static readonly Regex Pattern = new Regex(@"(?[^ :{}]+)(?:[^}]+)?}", RegexOptions.Compiled); +#endif /// /// Some logging frameworks support structured logging, such as serilog. This will allow you to add names to structured data in a format string: @@ -1871,24 +2099,8 @@ public static Func SimulateStructuredLogging(Func 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); - } + IEnumerable patternMatches; + return FormatStructuredMessage(targetMessage, formatParameters, out patternMatches); }; } @@ -1901,10 +2113,62 @@ private static string ReplaceFirst(string text, string search, string replace) } return text.Substring(0, pos) + replace + text.Substring(pos + search.Length); } + + public static string FormatStructuredMessage(string targetMessage, object[] formatParameters, out IEnumerable patternMatches) + { + if (formatParameters.Length == 0) + { + patternMatches = Enumerable.Empty(); + return targetMessage; + } + + List processedArguments = new List(); + patternMatches = processedArguments; + + foreach (Match match in Pattern.Matches(targetMessage)) + { + var arg = match.Groups["arg"].Value; + + int notUsed; + if (!int.TryParse(arg, out notUsed)) + { + int argumentIndex = processedArguments.IndexOf(arg); + if (argumentIndex == -1) + { + argumentIndex = processedArguments.Count; + processedArguments.Add(arg); + } + + targetMessage = ReplaceFirst(targetMessage, match.Value, + "{" + argumentIndex + match.Groups["format"].Value + "}"); + } + } + 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); + } + } } internal static class TypeExtensions { + internal static ConstructorInfo GetConstructorPortable(this Type type, params Type[] types) + { +#if LIBLOG_PORTABLE + return type.GetTypeInfo().DeclaredConstructors.FirstOrDefault + (constructor => + constructor.GetParameters() + .Select(parameter => parameter.ParameterType) + .SequenceEqual(types)); +#else + return type.GetConstructor(types); +#endif + } + internal static MethodInfo GetMethodPortable(this Type type, string name) { #if LIBLOG_PORTABLE diff --git a/src/net45/WampSharp/packages.config b/src/net45/WampSharp/packages.config index dac833a01..50c62a78c 100644 --- a/src/net45/WampSharp/packages.config +++ b/src/net45/WampSharp/packages.config @@ -1,11 +1,11 @@  - + - + \ No newline at end of file