From d0b2f00884cbd06406f1d5b342679001d9cd573e Mon Sep 17 00:00:00 2001 From: Peter Mounce Date: Thu, 30 Jun 2016 00:03:01 +0100 Subject: [PATCH 1/3] Replace log4net dependency with LibLog This allows consumers to use any log library supported by LibLog, rather than requiring a log4net dependency. --- .../App_Packages/LibLog.4.2/LibLog.cs | 1999 +++++++++++++++++ .../Medidata.ZipkinTracer.Core.csproj | 9 +- .../Middlewares/ZipkinMiddleware.cs | 5 +- .../SpanCollector.cs | 10 +- .../SpanProcessor.cs | 8 +- .../SpanProcessorTaskFactory.cs | 8 +- .../ZipkinClient.cs | 27 +- .../packages.config | 2 +- .../Medidata.ZipkinTracer.Core.Test.csproj | 4 - .../SpanCollectorTests.cs | 12 +- .../SpanProcessorTests.cs | 8 +- .../SpanProcesssorTaskFactoryTests.cs | 7 +- .../SpanTracerTests.cs | 4 +- .../ZipkinClientTests.cs | 25 +- .../packages.config | 1 - 15 files changed, 2055 insertions(+), 74 deletions(-) create mode 100644 src/Medidata.ZipkinTracer.Core/App_Packages/LibLog.4.2/LibLog.cs diff --git a/src/Medidata.ZipkinTracer.Core/App_Packages/LibLog.4.2/LibLog.cs b/src/Medidata.ZipkinTracer.Core/App_Packages/LibLog.4.2/LibLog.cs new file mode 100644 index 0000000..45e6da7 --- /dev/null +++ b/src/Medidata.ZipkinTracer.Core/App_Packages/LibLog.4.2/LibLog.cs @@ -0,0 +1,1999 @@ +//=============================================================================== +// 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 +// +// Define LIBLOG_PUBLIC to enable ability to GET a logger (LogProvider.For<>() etc) from outside this library. NOTE: +// this can have unintended consequences of consumers of your library using your library to resolve a logger. If the +// reason is because you want to open this functionality to other projects within your solution, +// consider [InternalsVisibleTo] instead. +// +// Define LIBLOG_PROVIDERS_ONLY if your library provides its own logging API and you just want to use the +// LibLog providers internally to provide built in support for popular logging frameworks. + +#pragma warning disable 1591 + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Medidata.ZipkinTracer.Core.Logging")] +[assembly: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Medidata.ZipkinTracer.Core.Logging.Logger.#Invoke(Medidata.ZipkinTracer.Core.Logging.LogLevel,System.Func`1,System.Exception,System.Object[])")] + +// If you copied this file manually, you need to change all "YourRootNameSpace" so not to clash with other libraries +// that use LibLog +#if LIBLOG_PROVIDERS_ONLY +namespace Medidata.ZipkinTracer.Core.LibLog +#else +namespace Medidata.ZipkinTracer.Core.Logging +#endif +{ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; +#if LIBLOG_PROVIDERS_ONLY + using Medidata.ZipkinTracer.Core.LibLog.LogProviders; +#else + using Medidata.ZipkinTracer.Core.Logging.LogProviders; +#endif + using System; +#if !LIBLOG_PROVIDERS_ONLY + using System.Diagnostics; +#if !LIBLOG_PORTABLE + using System.Runtime.CompilerServices; +#endif +#endif + +#if LIBLOG_PROVIDERS_ONLY + internal +#else + public +#endif + delegate bool Logger(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters); + +#if !LIBLOG_PROVIDERS_ONLY + /// + /// Simple interface that represent a logger. + /// +#if LIBLOG_PUBLIC + public +#else + internal +#endif + 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 ); + } +#endif + + /// + /// The log level. + /// +#if LIBLOG_PROVIDERS_ONLY + internal +#else + public +#endif + enum LogLevel + { + Trace, + Debug, + Info, + Warn, + Error, + Fatal + } + +#if !LIBLOG_PROVIDERS_ONLY +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static partial 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) + { + GuardAgainstNullLogger(logger); + 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); + } + } + + // ReSharper disable once UnusedParameter.Local + 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; + } + } +#endif + + /// + /// Represents a way to get a + /// +#if LIBLOG_PROVIDERS_ONLY + internal +#else + public +#endif + interface ILogProvider + { + /// + /// Gets the specified named logger. + /// + /// Name of the logger. + /// The logger reference. + Logger 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. + /// +#if LIBLOG_PROVIDERS_ONLY + internal +#else + public +#endif + 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 = "Medidata.ZipkinTracer.Core_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; + private static Action s_onCurrentLogProviderSet; + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static LogProvider() + { + IsDisabled = false; + } + + /// + /// Sets the current log provider. + /// + /// The log provider. + public static void SetCurrentLogProvider(ILogProvider logProvider) + { + s_currentLogProvider = logProvider; + + RaiseOnCurrentLogProviderSet(); + } + + /// + /// Gets or sets a value indicating whether this is logging is disabled. + /// + /// + /// true if logging is disabled; otherwise, false. + /// + public static bool IsDisabled { get; set; } + + /// + /// Sets an action that is invoked when a consumer of your library has called SetCurrentLogProvider. It is + /// important that hook into this if you are using child libraries (especially ilmerged ones) that are using + /// LibLog (or other logging abstraction) so you adapt and delegate to them. + /// + /// + internal static Action OnCurrentLogProviderSet + { + set + { + s_onCurrentLogProviderSet = value; + RaiseOnCurrentLogProviderSet(); + } + } + + internal static ILogProvider CurrentLogProvider + { + get + { + return s_currentLogProvider; + } + } + + /// + /// Gets a logger for the specified type. + /// + /// The type whose name will be used for the logger. + /// An instance of +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static ILog For() + { + return GetLogger(typeof(T)); + } + +#if !LIBLOG_PORTABLE + /// + /// Gets a logger for the current class. + /// + /// An instance of + [MethodImpl(MethodImplOptions.NoInlining)] +#if LIBLOG_PUBLIC + public +#else + internal +#endif + 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 +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static ILog GetLogger(Type type) + { + return GetLogger(type.FullName); + } + + /// + /// Gets a logger with the specified name. + /// + /// The name. + /// An instance of +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static ILog GetLogger(string name) + { + ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); + return logProvider == null + ? NoOpLogger.Instance + : (ILog)new LoggerExecutionWrapper(logProvider.GetLogger(name), () => IsDisabled); + } + + /// + /// Opens a nested diagnostics context. + /// + /// A message. + /// An that closes context when disposed. + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")] +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static IDisposable OpenNestedContext(string message) + { + ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); + + return logProvider == null + ? new DisposableAction(() => { }) + : logProvider.OpenNestedContext(message); + } + + /// + /// Opens a mapped diagnostics context. + /// + /// A key. + /// A value. + /// An that closes context when disposed. + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")] +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static IDisposable OpenMappedContext(string key, string value) + { + ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); + + return logProvider == null + ? new DisposableAction(() => { }) + : logProvider.OpenMappedContext(key, value); + } +#endif + +#if LIBLOG_PROVIDERS_ONLY + private +#else + internal +#endif + delegate bool IsLoggerAvailable(); + +#if LIBLOG_PROVIDERS_ONLY + private +#else + internal +#endif + delegate ILogProvider CreateLogProvider(); + +#if LIBLOG_PROVIDERS_ONLY + private +#else + internal +#endif + 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()), + }; + +#if !LIBLOG_PROVIDERS_ONLY + private static void RaiseOnCurrentLogProviderSet() + { + if (s_onCurrentLogProviderSet != null) + { + s_onCurrentLogProviderSet(s_currentLogProvider); + } + } +#endif + + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.WriteLine(System.String,System.Object,System.Object)")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + internal 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 occurred resolving a log provider. Logging for this assembly {0} is disabled. {1}", + typeof(LogProvider).GetAssemblyPortable().FullName, + ex); + } + return null; + } + +#if !LIBLOG_PROVIDERS_ONLY + internal class NoOpLogger : ILog + { + internal static readonly NoOpLogger Instance = new NoOpLogger(); + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + return false; + } + } +#endif + } + +#if !LIBLOG_PROVIDERS_ONLY + internal class LoggerExecutionWrapper : ILog + { + private readonly Logger _logger; + private readonly Func _getIsDisabled; + internal const string FailedToGenerateLogMessage = "Failed to generate log message"; + + internal LoggerExecutionWrapper(Logger logger, Func getIsDisabled = null) + { + _logger = logger; + _getIsDisabled = getIsDisabled ?? (() => false); + } + + internal Logger WrappedLogger + { + get { return _logger; } + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters) + { + if (_getIsDisabled()) + { + 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); + } + + Func wrappedMessageFunc = () => + { + try + { + return messageFunc(); + } + catch (Exception ex) + { + Log(LogLevel.Error, () => FailedToGenerateLogMessage, ex); + } + return null; + }; + return _logger(logLevel, wrappedMessageFunc, exception, formatParameters); + } + } +#endif +} + +#if LIBLOG_PROVIDERS_ONLY +namespace Medidata.ZipkinTracer.Core.LibLog.LogProviders +#else +namespace Medidata.ZipkinTracer.Core.Logging.LogProviders +#endif +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; +#if !LIBLOG_PORTABLE + using System.Diagnostics; +#endif + using System.Globalization; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; +#if !LIBLOG_PORTABLE + using System.Text; +#endif + 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 Logger 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 s_providerIsAvailableOverride = true; + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "NLog")] + public NLogLogProvider() + { + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("NLog.LogManager not found"); + } + _getLoggerByNameDelegate = GetGetLoggerMethodCall(); + } + + public static bool ProviderIsAvailableOverride + { + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new NLogLogger(_getLoggerByNameDelegate(name)).Log; + } + + public static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && GetLogManagerType() != null; + } + + protected override OpenNdc GetOpenNdcMethod() + { + Type ndcContextType = Type.GetType("NLog.NestedDiagnosticsContext, NLog"); + MethodInfo pushMethod = ndcContextType.GetMethodPortable("Push", 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", typeof(string), typeof(string)); + MethodInfo removeMethod = mdcContextType.GetMethodPortable("Remove", 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", typeof(string)); + ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); + MethodCallExpression methodCall = Expression.Call(null, method, nameParam); + return Expression.Lambda>(methodCall, nameParam).Compile(); + } + + internal class NLogLogger + { + private readonly dynamic _logger; + + internal NLogLogger(dynamic logger) + { + _logger = logger; + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + 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; + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + 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 s_providerIsAvailableOverride = true; + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")] + public Log4NetLogProvider() + { + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("log4net.LogManager not found"); + } + _getLoggerByNameDelegate = GetGetLoggerMethodCall(); + } + + public static bool ProviderIsAvailableOverride + { + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new Log4NetLogger(_getLoggerByNameDelegate(name)).Log; + } + + internal static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && GetLogManagerType() != null; + } + + protected override OpenNdc GetOpenNdcMethod() + { + Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net"); + PropertyInfo stacksProperty = logicalThreadContextType.GetPropertyPortable("Stacks"); + Type logicalThreadContextStacksType = stacksProperty.PropertyType; + PropertyInfo stacksIndexerProperty = logicalThreadContextStacksType.GetPropertyPortable("Item"); + Type stackType = stacksIndexerProperty.PropertyType; + MethodInfo pushMethod = stackType.GetMethodPortable("Push"); + + ParameterExpression messageParameter = + Expression.Parameter(typeof(string), "message"); + + // message => LogicalThreadContext.Stacks.Item["NDC"].Push(message); + MethodCallExpression callPushBody = + Expression.Call( + Expression.Property(Expression.Property(null, stacksProperty), + stacksIndexerProperty, + Expression.Constant("NDC")), + pushMethod, + messageParameter); + + OpenNdc result = + Expression.Lambda(callPushBody, messageParameter) + .Compile(); + + return result; + } + + protected override OpenMdc GetOpenMdcMethod() + { + Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net"); + PropertyInfo propertiesProperty = logicalThreadContextType.GetPropertyPortable("Properties"); + Type logicalThreadContextPropertiesType = propertiesProperty.PropertyType; + PropertyInfo propertiesIndexerProperty = logicalThreadContextPropertiesType.GetPropertyPortable("Item"); + + MethodInfo removeMethod = logicalThreadContextPropertiesType.GetMethodPortable("Remove"); + + ParameterExpression keyParam = Expression.Parameter(typeof(string), "key"); + ParameterExpression valueParam = Expression.Parameter(typeof(string), "value"); + + MemberExpression propertiesExpression = Expression.Property(null, propertiesProperty); + + // (key, value) => LogicalThreadContext.Properties.Item[key] = value; + BinaryExpression setProperties = Expression.Assign(Expression.Property(propertiesExpression, propertiesIndexerProperty, keyParam), valueParam); + + // key => LogicalThreadContext.Properties.Remove(key); + MethodCallExpression removeMethodCall = Expression.Call(propertiesExpression, removeMethod, keyParam); + + Action set = Expression + .Lambda>(setProperties, 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", typeof(string)); + ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); + MethodCallExpression methodCall = Expression.Call(null, method, nameParam); + return Expression.Lambda>(methodCall, nameParam).Compile(); + } + + internal class Log4NetLogger + { + private readonly dynamic _logger; + private static Type s_callerStackBoundaryType; + private static readonly object CallerStackBoundaryTypeSync = new object(); + + private readonly object _levelDebug; + private readonly object _levelInfo; + private readonly object _levelWarn; + private readonly object _levelError; + private readonly object _levelFatal; + private readonly Func _isEnabledForDelegate; + private readonly Action _logDelegate; + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "ILogger")] + internal Log4NetLogger(dynamic logger) + { + _logger = logger.Logger; + + var logEventLevelType = Type.GetType("log4net.Core.Level, log4net"); + if (logEventLevelType == null) + { + throw new InvalidOperationException("Type log4net.Core.Level was not found."); + } + + var levelFields = logEventLevelType.GetFieldsPortable().ToList(); + _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); + + // Func isEnabledFor = (logger, level) => { return ((log4net.Core.ILogger)logger).IsEnabled(level); } + var loggerType = Type.GetType("log4net.Core.ILogger, log4net"); + if (loggerType == null) + { + 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(); + + // Action Log = + // (logger, callerStackBoundaryDeclaringType, level, message, exception) => { ((ILogger)logger).Write(callerStackBoundaryDeclaringType, level, message, exception); } + MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("Log", + typeof(Type), + logEventLevelType, + typeof(string), + typeof(Exception)); + ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); + var writeMethodExp = Expression.Call( + instanceCast, + writeExceptionMethodInfo, + callerStackBoundaryDeclaringTypeParam, + levelCast, + messageParam, + exceptionParam); + _logDelegate = Expression.Lambda>( + writeMethodExp, + instanceParam, + callerStackBoundaryDeclaringTypeParam, + levelParam, + messageParam, + exceptionParam).Compile(); + } + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + if (messageFunc == null) + { + return IsLogLevelEnable(logLevel); + } + + if (!IsLogLevelEnable(logLevel)) + { + return false; + } + + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + + // determine correct caller - this might change due to jit optimizations with method inlining + if (s_callerStackBoundaryType == null) + { + lock (CallerStackBoundaryTypeSync) + { +#if !LIBLOG_PORTABLE + StackTrace stack = new StackTrace(); + Type thisType = GetType(); + s_callerStackBoundaryType = Type.GetType("LoggerExecutionWrapper"); + for (var i = 1; i < stack.FrameCount; i++) + { + if (!IsInTypeHierarchy(thisType, stack.GetFrame(i).GetMethod().DeclaringType)) + { + s_callerStackBoundaryType = stack.GetFrame(i - 1).GetMethod().DeclaringType; + break; + } + } +#else + s_callerStackBoundaryType = typeof (LoggerExecutionWrapper); +#endif + } + } + + var translatedLevel = TranslateLevel(logLevel); + _logDelegate(_logger, s_callerStackBoundaryType, translatedLevel, messageFunc(), exception); + return true; + } + + private static bool IsInTypeHierarchy(Type currentType, Type checkType) + { + while (currentType != null && currentType != typeof(object)) + { + if (currentType == checkType) + { + return true; + } + currentType = currentType.GetBaseTypePortable(); + } + return false; + } + + private bool IsLogLevelEnable(LogLevel logLevel) + { + var level = TranslateLevel(logLevel); + return _isEnabledForDelegate(_logger, level); + } + + private object TranslateLevel(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Trace: + 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); + } + } + } + } + + internal class EntLibLogProvider : LogProviderBase + { + private const string TypeTemplate = "Microsoft.Practices.EnterpriseLibrary.Logging.{0}, Microsoft.Practices.EnterpriseLibrary.Logging"; + private static bool s_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; + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static EntLibLogProvider() + { + LogEntryType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, "LogEntry")); + LoggerType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, "Logger")); + TraceEventTypeType = TraceEventTypeValues.Type; + if (LogEntryType == null + || TraceEventTypeType == null + || LoggerType == null) + { + return; + } + WriteLogEntry = GetWriteLogEntry(); + ShouldLogEntry = GetShouldLogEntry(); + } + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "EnterpriseLibrary")] + public EntLibLogProvider() + { + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("Microsoft.Practices.EnterpriseLibrary.Logging.Logger not found"); + } + } + + public static bool ProviderIsAvailableOverride + { + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new EntLibLogger(name, WriteLogEntry, ShouldLogEntry).Log; + } + + 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", 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", 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), + 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", typeof (string)), + logNameParameter))); + return memberInit; + } + + internal class EntLibLogger + { + 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 s_providerIsAvailableOverride = true; + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "Serilog")] + public SerilogLogProvider() + { + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("Serilog.Log not found"); + } + _getLoggerByNameDelegate = GetForContextMethodCall(); + } + + public static bool ProviderIsAvailableOverride + { + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new SerilogLogger(_getLoggerByNameDelegate(name)).Log; + } + + 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", + 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", 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("SourceContext", name, false); + } + + internal class SerilogLogger + { + 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; + + [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "ILogger")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogEventLevel")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "Serilog")] + static SerilogLogger() + { + var logEventLevelType = Type.GetType("Serilog.Events.LogEventLevel, Serilog"); + if (logEventLevelType == null) + { + throw new InvalidOperationException("Type Serilog.Events.LogEventLevel was not found."); + } + DebugLevel = Enum.Parse(logEventLevelType, "Debug", false); + ErrorLevel = Enum.Parse(logEventLevelType, "Error", false); + FatalLevel = Enum.Parse(logEventLevelType, "Fatal", false); + InformationLevel = Enum.Parse(logEventLevelType, "Information", false); + VerboseLevel = Enum.Parse(logEventLevelType, "Verbose", false); + WarningLevel = Enum.Parse(logEventLevelType, "Warning", false); + + // Func isEnabled = (logger, level) => { return ((SeriLog.ILogger)logger).IsEnabled(level); } + var loggerType = Type.GetType("Serilog.ILogger, Serilog"); + if (loggerType == null) + { + throw new InvalidOperationException("Type Serilog.ILogger was not found."); + } + 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, logEventLevelType); + 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", logEventLevelType, 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", + logEventLevelType, + 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) + { + var translatedLevel = TranslateLevel(logLevel); + if (messageFunc == null) + { + return IsEnabled(_logger, translatedLevel); + } + + if (!IsEnabled(_logger, translatedLevel)) + { + return false; + } + + if (exception != null) + { + LogException(translatedLevel, messageFunc, exception, formatParameters); + } + else + { + LogMessage(translatedLevel, messageFunc, formatParameters); + } + + return true; + } + + private void LogMessage(object translatedLevel, Func messageFunc, object[] formatParameters) + { + Write(_logger, translatedLevel, messageFunc(), formatParameters); + } + + private void LogException(object logLevel, Func messageFunc, Exception exception, object[] formatParams) + { + WriteException(_logger, logLevel, exception, messageFunc(), formatParams); + } + + private static object TranslateLevel(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Fatal: + return FatalLevel; + case LogLevel.Error: + return ErrorLevel; + case LogLevel.Warn: + return WarningLevel; + case LogLevel.Info: + return InformationLevel; + case LogLevel.Trace: + return VerboseLevel; + default: + return DebugLevel; + } + } + } + } + + 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 s_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 s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new LoupeLogger(name, _logWriteDelegate).Log; + } + + 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", + 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; + } + + internal class LoupeLogger + { + 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; +#if DEBUG + _skipLevel = 2; +#else + _skipLevel = 1; +#endif + } + + 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 static 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; + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + 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 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 {curly braces} 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 || formatParameters.Length == 0) + { + 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.GetRuntimeMethods().SingleOrDefault(m => m.Name == name); +#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 + } + + internal static IEnumerable GetFieldsPortable(this Type type) + { +#if LIBLOG_PORTABLE + return type.GetRuntimeFields(); +#else + return type.GetFields(); +#endif + } + + internal static Type GetBaseTypePortable(this Type type) + { +#if LIBLOG_PORTABLE + return type.GetTypeInfo().BaseType; +#else + return type.BaseType; +#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/Medidata.ZipkinTracer.Core/Medidata.ZipkinTracer.Core.csproj b/src/Medidata.ZipkinTracer.Core/Medidata.ZipkinTracer.Core.csproj index 5b9d641..14d6a8c 100644 --- a/src/Medidata.ZipkinTracer.Core/Medidata.ZipkinTracer.Core.csproj +++ b/src/Medidata.ZipkinTracer.Core/Medidata.ZipkinTracer.Core.csproj @@ -19,7 +19,7 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG;LIBLOG_PUBLIC prompt 4 @@ -27,15 +27,11 @@ pdbonly true bin\Release\ - TRACE + TRACE;LIBLOG_PUBLIC prompt 4 - - ..\..\packages\log4net.2.0.5\lib\net45-full\log4net.dll - True - ..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll True @@ -60,6 +56,7 @@ + diff --git a/src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs b/src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs index 7093ff8..1340119 100644 --- a/src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs +++ b/src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using log4net; +using Medidata.ZipkinTracer.Core.Logging; using Microsoft.Owin; using Owin; @@ -22,8 +22,7 @@ public override async Task Invoke(IOwinContext context) return; } - var logger = LogManager.GetLogger("ZipkinMiddleware"); - var zipkin = new ZipkinClient(logger, _config, context); + var zipkin = new ZipkinClient(_config, context); var span = zipkin.StartServerTrace(context.Request.Uri, context.Request.Method); await Next.Invoke(context); zipkin.EndServerTrace(span); diff --git a/src/Medidata.ZipkinTracer.Core/SpanCollector.cs b/src/Medidata.ZipkinTracer.Core/SpanCollector.cs index 9446cdd..098e62e 100644 --- a/src/Medidata.ZipkinTracer.Core/SpanCollector.cs +++ b/src/Medidata.ZipkinTracer.Core/SpanCollector.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Concurrent; -using log4net; +using Medidata.ZipkinTracer.Core.Logging; using Medidata.ZipkinTracer.Models; namespace Medidata.ZipkinTracer.Core @@ -14,24 +14,24 @@ public class SpanCollector private static SpanCollector instance; - public static SpanCollector GetInstance(Uri uri, uint maxProcessorBatchSize, ILog logger) + public static SpanCollector GetInstance(Uri uri, uint maxProcessorBatchSize) { if (instance == null) { - instance = new SpanCollector(uri, maxProcessorBatchSize, logger); + instance = new SpanCollector(uri, maxProcessorBatchSize); instance.Start(); } return instance; } - public SpanCollector(Uri uri, uint maxProcessorBatchSize, ILog logger) + public SpanCollector(Uri uri, uint maxProcessorBatchSize) { if ( spanQueue == null) { spanQueue = new BlockingCollection(MAX_QUEUE_SIZE); } - spanProcessor = new SpanProcessor(uri, spanQueue, maxProcessorBatchSize, logger); + spanProcessor = new SpanProcessor(uri, spanQueue, maxProcessorBatchSize); } public virtual void Collect(Span span) diff --git a/src/Medidata.ZipkinTracer.Core/SpanProcessor.cs b/src/Medidata.ZipkinTracer.Core/SpanProcessor.cs index a502680..bc23d33 100644 --- a/src/Medidata.ZipkinTracer.Core/SpanProcessor.cs +++ b/src/Medidata.ZipkinTracer.Core/SpanProcessor.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using System.Net; -using log4net; +using Medidata.ZipkinTracer.Core.Logging; using Medidata.ZipkinTracer.Models; using Newtonsoft.Json; @@ -28,7 +28,7 @@ public class SpanProcessor internal uint maxBatchSize; private readonly ILog logger; - public SpanProcessor(Uri uri, BlockingCollection spanQueue, uint maxBatchSize, ILog logger) + public SpanProcessor(Uri uri, BlockingCollection spanQueue, uint maxBatchSize) { if ( spanQueue == null) { @@ -44,8 +44,8 @@ public SpanProcessor(Uri uri, BlockingCollection spanQueue, uint maxBatchS this.spanQueue = spanQueue; this.serializableSpans = new ConcurrentQueue(); this.maxBatchSize = maxBatchSize; - this.logger = logger; - spanProcessorTaskFactory = new SpanProcessorTaskFactory(logger); + this.logger = LogProvider.GetCurrentClassLogger(); + spanProcessorTaskFactory = new SpanProcessorTaskFactory(null); } public virtual void Stop() diff --git a/src/Medidata.ZipkinTracer.Core/SpanProcessorTaskFactory.cs b/src/Medidata.ZipkinTracer.Core/SpanProcessorTaskFactory.cs index bf55444..01503a5 100644 --- a/src/Medidata.ZipkinTracer.Core/SpanProcessorTaskFactory.cs +++ b/src/Medidata.ZipkinTracer.Core/SpanProcessorTaskFactory.cs @@ -1,8 +1,8 @@ -using log4net; -using System; +using System; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; +using Medidata.ZipkinTracer.Core.Logging; namespace Medidata.ZipkinTracer.Core { @@ -16,7 +16,7 @@ public class SpanProcessorTaskFactory public SpanProcessorTaskFactory(ILog logger, CancellationTokenSource cancellationTokenSource = null) { - this.logger = logger; + this.logger = logger ?? LogProvider.GetCurrentClassLogger(); if (cancellationTokenSource == null) { @@ -55,7 +55,7 @@ internal async void ActionWrapper(Action action) } catch (Exception ex) { - logger.Error("Error in SpanProcessorTask", ex); + logger.ErrorException("Error in SpanProcessorTask", ex); delayTime = encounteredAnErrorDelayTime; } diff --git a/src/Medidata.ZipkinTracer.Core/ZipkinClient.cs b/src/Medidata.ZipkinTracer.Core/ZipkinClient.cs index b4d5b6b..cd7122c 100644 --- a/src/Medidata.ZipkinTracer.Core/ZipkinClient.cs +++ b/src/Medidata.ZipkinTracer.Core/ZipkinClient.cs @@ -1,6 +1,6 @@ using System; using System.Runtime.CompilerServices; -using log4net; +using Medidata.ZipkinTracer.Core.Logging; using Microsoft.Owin; using Medidata.ZipkinTracer.Models; @@ -19,12 +19,10 @@ public class ZipkinClient: ITracerClient public IZipkinConfig ZipkinConfig { get; } - public ZipkinClient(ILog logger, IZipkinConfig zipkinConfig, IOwinContext context, SpanCollector collector = null) + public ZipkinClient(IZipkinConfig zipkinConfig, IOwinContext context, SpanCollector collector = null) { - if (logger == null) throw new ArgumentNullException(nameof(logger)); if (zipkinConfig == null) throw new ArgumentNullException(nameof(zipkinConfig)); if (context == null) throw new ArgumentNullException(nameof(context)); - var traceProvider = new TraceProvider(zipkinConfig, context); IsTraceOn = !zipkinConfig.Bypass(context.Request) && IsTraceProviderSamplingOn(traceProvider); @@ -33,14 +31,13 @@ public ZipkinClient(ILog logger, IZipkinConfig zipkinConfig, IOwinContext contex zipkinConfig.Validate(); ZipkinConfig = zipkinConfig; - this.logger = logger; + this.logger = LogProvider.GetCurrentClassLogger(); try { spanCollector = collector ?? SpanCollector.GetInstance( zipkinConfig.ZipkinBaseUri, - zipkinConfig.SpanProcessorBatchSize, - logger); + zipkinConfig.SpanProcessorBatchSize); spanTracer = new SpanTracer( spanCollector, @@ -52,7 +49,7 @@ public ZipkinClient(ILog logger, IZipkinConfig zipkinConfig, IOwinContext contex } catch (Exception ex) { - logger.Error("Error Building Zipkin Client Provider", ex); + logger.Log(LogLevel.Error, () => "Error Building Zipkin Client Provider", ex); IsTraceOn = false; } } @@ -73,7 +70,7 @@ public Span StartClientTrace(Uri remoteUri, string methodName, ITraceProvider tr } catch (Exception ex) { - logger.Error("Error Starting Client Trace", ex); + logger.Log(LogLevel.Error, () => "Error Starting Client Trace", ex); return null; } } @@ -89,7 +86,7 @@ public void EndClientTrace(Span clientSpan, int statusCode) } catch (Exception ex) { - logger.Error("Error Ending Client Trace", ex); + logger.Log(LogLevel.Error, () => "Error Ending Client Trace", ex); } } @@ -109,7 +106,7 @@ public Span StartServerTrace(Uri requestUri, string methodName) } catch (Exception ex) { - logger.Error("Error Starting Server Trace", ex); + logger.Log(LogLevel.Error, () => "Error Starting Server Trace", ex); return null; } } @@ -125,7 +122,7 @@ public void EndServerTrace(Span serverSpan) } catch (Exception ex) { - logger.Error("Error Ending Server Trace", ex); + logger.Log(LogLevel.Error, () => "Error Ending Server Trace", ex); } } @@ -146,7 +143,7 @@ public void Record(Span span, [CallerMemberName] string value = null) } catch (Exception ex) { - logger.Error("Error recording the annotation", ex); + logger.Log(LogLevel.Error, () => "Error recording the annotation", ex); } } @@ -175,7 +172,7 @@ public void RecordBinary(Span span, string key, T value) } catch (Exception ex) { - logger.Error($"Error recording a binary annotation (key: {key})", ex); + logger.Log(LogLevel.Error, () => $"Error recording a binary annotation (key: {key})", ex); } } @@ -195,7 +192,7 @@ public void RecordLocalComponent(Span span, string value) } catch (Exception ex) { - logger.Error($"Error recording local trace (value: {value})", ex); + logger.Log(LogLevel.Error, () => $"Error recording local trace (value: {value})", ex); } } diff --git a/src/Medidata.ZipkinTracer.Core/packages.config b/src/Medidata.ZipkinTracer.Core/packages.config index cb4b651..8d39287 100644 --- a/src/Medidata.ZipkinTracer.Core/packages.config +++ b/src/Medidata.ZipkinTracer.Core/packages.config @@ -1,6 +1,6 @@  - + diff --git a/tests/Medidata.ZipkinTracer.Core.Test/Medidata.ZipkinTracer.Core.Test.csproj b/tests/Medidata.ZipkinTracer.Core.Test/Medidata.ZipkinTracer.Core.Test.csproj index eda84c6..4b1e7e7 100644 --- a/tests/Medidata.ZipkinTracer.Core.Test/Medidata.ZipkinTracer.Core.Test.csproj +++ b/tests/Medidata.ZipkinTracer.Core.Test/Medidata.ZipkinTracer.Core.Test.csproj @@ -37,10 +37,6 @@ 4 - - ..\..\packages\log4net.2.0.5\lib\net45-full\log4net.dll - True - ..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll diff --git a/tests/Medidata.ZipkinTracer.Core.Test/SpanCollectorTests.cs b/tests/Medidata.ZipkinTracer.Core.Test/SpanCollectorTests.cs index 39db856..1e7e771 100644 --- a/tests/Medidata.ZipkinTracer.Core.Test/SpanCollectorTests.cs +++ b/tests/Medidata.ZipkinTracer.Core.Test/SpanCollectorTests.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Concurrent; -using log4net; +using Medidata.ZipkinTracer.Core.Logging; using Medidata.ZipkinTracer.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; using Ploeh.AutoFixture; @@ -28,7 +28,7 @@ public void GetInstance() { SpanCollector.spanQueue = null; - spanCollector = SpanCollector.GetInstance(new Uri("http://localhost"), 0, logger); + spanCollector = SpanCollector.GetInstance(new Uri("http://localhost"), 0); Assert.IsNotNull(SpanCollector.spanQueue); } @@ -38,7 +38,7 @@ public void CTOR_initializesSpanCollector() { SpanCollector.spanQueue = null; - spanCollector = new SpanCollector(new Uri("http://localhost"), 0, logger); + spanCollector = new SpanCollector(new Uri("http://localhost"), 0); Assert.IsNotNull(SpanCollector.spanQueue); } @@ -49,7 +49,7 @@ public void CTOR_doesntReinitializeSpanCollector() var spanQueue = new BlockingCollection(); SpanCollector.spanQueue = spanQueue; - spanCollector = new SpanCollector(new Uri("http://localhost"), 0, logger); + spanCollector = new SpanCollector(new Uri("http://localhost"), 0); Assert.IsTrue(System.Object.ReferenceEquals(SpanCollector.spanQueue, spanQueue)); } @@ -100,11 +100,11 @@ public void StopProcessingSpans() private void SetupSpanCollector() { - spanCollector = new SpanCollector(new Uri("http://localhost"), 0, logger); + spanCollector = new SpanCollector(new Uri("http://localhost"), 0); SpanCollector.spanQueue = fixture.Create>(); spanProcessorStub = MockRepository.GenerateStub(new Uri("http://localhost"), - SpanCollector.spanQueue, (uint)0, logger); + SpanCollector.spanQueue, (uint)0); spanCollector.spanProcessor = spanProcessorStub; } } diff --git a/tests/Medidata.ZipkinTracer.Core.Test/SpanProcessorTests.cs b/tests/Medidata.ZipkinTracer.Core.Test/SpanProcessorTests.cs index 9aa45da..ef83288 100644 --- a/tests/Medidata.ZipkinTracer.Core.Test/SpanProcessorTests.cs +++ b/tests/Medidata.ZipkinTracer.Core.Test/SpanProcessorTests.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Concurrent; -using log4net; +using Medidata.ZipkinTracer.Core.Logging; using Medidata.ZipkinTracer.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; using Ploeh.AutoFixture; @@ -26,7 +26,7 @@ public void Init() logger = MockRepository.GenerateStub(); queue = new BlockingCollection(); testMaxBatchSize = 10; - spanProcessor = MockRepository.GenerateStub(new Uri("http://localhost"), queue, testMaxBatchSize, logger); + spanProcessor = MockRepository.GenerateStub(new Uri("http://localhost"), queue, testMaxBatchSize); spanProcessor.Stub(x => x.SendSpansToZipkin(Arg.Is.Anything)).WhenCalled(s => { }); taskFactory = MockRepository.GenerateStub(logger, null); spanProcessor.spanProcessorTaskFactory = taskFactory; @@ -36,14 +36,14 @@ public void Init() [ExpectedException(typeof(ArgumentNullException))] public void CTOR_WithNullSpanQueue() { - new SpanProcessor(new Uri("http://localhost"), null, fixture.Create(), logger); + new SpanProcessor(new Uri("http://localhost"), null, fixture.Create()); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void CTOR_WithNullZipkinServer() { - new SpanProcessor(null, queue, fixture.Create(), logger); + new SpanProcessor(null, queue, fixture.Create()); } [TestMethod] diff --git a/tests/Medidata.ZipkinTracer.Core.Test/SpanProcesssorTaskFactoryTests.cs b/tests/Medidata.ZipkinTracer.Core.Test/SpanProcesssorTaskFactoryTests.cs index 7257e4c..e656abd 100644 --- a/tests/Medidata.ZipkinTracer.Core.Test/SpanProcesssorTaskFactoryTests.cs +++ b/tests/Medidata.ZipkinTracer.Core.Test/SpanProcesssorTaskFactoryTests.cs @@ -1,6 +1,6 @@ using System; using System.Threading; -using log4net; +using Medidata.ZipkinTracer.Core.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Rhino.Mocks; @@ -61,8 +61,9 @@ public void ActionWrapper_Exception() { Exception ex = new Exception("Exception!"); bool logErrorCalled = false; - logger.Stub(x => x.Error(Arg.Is.Equal("Error in SpanProcessorTask"), Arg.Is.Equal(ex))) - .WhenCalled(x => { logErrorCalled = true; }); + logger.Stub(x => x.Log(Arg.Is.Equal(LogLevel.Error), Arg>.Is.Null, Arg.Is.Null, Arg.Is.Null)).Return(true); + logger.Stub(x => x.ErrorException("Error in SpanProcessorTask", ex)) + .WhenCalled(x => { logErrorCalled = true; }).Return(true); var myAction = new Action(() => { actionCalled = true; throw ex; }); Assert.IsFalse(actionCalled); diff --git a/tests/Medidata.ZipkinTracer.Core.Test/SpanTracerTests.cs b/tests/Medidata.ZipkinTracer.Core.Test/SpanTracerTests.cs index e0a5f44..283efa5 100644 --- a/tests/Medidata.ZipkinTracer.Core.Test/SpanTracerTests.cs +++ b/tests/Medidata.ZipkinTracer.Core.Test/SpanTracerTests.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using log4net; +using Medidata.ZipkinTracer.Core.Logging; using Medidata.ZipkinTracer.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; using Ploeh.AutoFixture; @@ -27,7 +27,7 @@ public void Init() { fixture = new Fixture(); logger = MockRepository.GenerateStub(); - spanCollectorStub = MockRepository.GenerateStub(new Uri("http://localhost"), (uint)0, logger); + spanCollectorStub = MockRepository.GenerateStub(new Uri("http://localhost"), (uint)0); zipkinEndpointStub = MockRepository.GenerateStub(); zipkinNotToBeDisplayedDomainList = new List {".xyz.net"}; serverServiceName = "xyz-sandbox"; diff --git a/tests/Medidata.ZipkinTracer.Core.Test/ZipkinClientTests.cs b/tests/Medidata.ZipkinTracer.Core.Test/ZipkinClientTests.cs index e349c42..eac510a 100644 --- a/tests/Medidata.ZipkinTracer.Core.Test/ZipkinClientTests.cs +++ b/tests/Medidata.ZipkinTracer.Core.Test/ZipkinClientTests.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using log4net; +using Medidata.ZipkinTracer.Core.Logging; using Medidata.ZipkinTracer.Models; using Microsoft.Owin; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -36,25 +36,18 @@ public void Init() request.Stub(x => x.Headers).Return(new HeaderDictionary(headers)); } - [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public void CTOR_WithNullLogger() - { - new ZipkinClient(null, new ZipkinConfig(), owinContext); - } - [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void CTOR_WithNullConfig() { - new ZipkinClient(logger, null, owinContext); + new ZipkinClient(null, owinContext); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void CTOR_WithNullContext() { - new ZipkinClient(logger, new ZipkinConfig(), null); + new ZipkinClient(new ZipkinConfig(), null); } [TestMethod] @@ -65,8 +58,8 @@ public void CTOR_WithTraceIdNullOrEmpty() AddTraceId(string.Empty); AddSampled(false); - spanCollectorStub = MockRepository.GenerateStub(new Uri("http://localhost"), (uint)0, logger); - var zipkinClient = new ZipkinClient(logger, zipkinConfigStub, owinContext, spanCollectorStub); + spanCollectorStub = MockRepository.GenerateStub(new Uri("http://localhost"), (uint)0); + var zipkinClient = new ZipkinClient(zipkinConfigStub, owinContext, spanCollectorStub); Assert.IsFalse(zipkinClient.IsTraceOn); } @@ -78,8 +71,8 @@ public void CTOR_WithIsSampledFalse() AddTraceId(fixture.Create()); AddSampled(false); - spanCollectorStub = MockRepository.GenerateStub(new Uri("http://localhost"), (uint)0, logger); - var zipkinClient = new ZipkinClient(logger, zipkinConfigStub, owinContext, spanCollectorStub); + spanCollectorStub = MockRepository.GenerateStub(new Uri("http://localhost"), (uint)0); + var zipkinClient = new ZipkinClient(zipkinConfigStub, owinContext, spanCollectorStub); Assert.IsFalse(zipkinClient.IsTraceOn); } @@ -517,7 +510,7 @@ public void RecordLocalComponent_IsTraceOnIsFalse_DoesNotAddLocalComponentAnnota private ITracerClient SetupZipkinClient(IZipkinConfig zipkinConfig = null) { - spanCollectorStub = MockRepository.GenerateStub(new Uri("http://localhost"), (uint)0, logger); + spanCollectorStub = MockRepository.GenerateStub(new Uri("http://localhost"), (uint)0); traceProvider.Stub(x => x.TraceId).Return(fixture.Create()); traceProvider.Stub(x => x.SpanId).Return(fixture.Create()); @@ -535,7 +528,7 @@ private ITracerClient SetupZipkinClient(IZipkinConfig zipkinConfig = null) zipkinConfigSetup = CreateZipkinConfigWithDefaultValues(); } - return new ZipkinClient(logger, zipkinConfigSetup, context, spanCollectorStub); + return new ZipkinClient(zipkinConfigSetup, context, spanCollectorStub); } private IZipkinConfig CreateZipkinConfigWithDefaultValues() diff --git a/tests/Medidata.ZipkinTracer.Core.Test/packages.config b/tests/Medidata.ZipkinTracer.Core.Test/packages.config index 5d2c7ff..cfa0197 100644 --- a/tests/Medidata.ZipkinTracer.Core.Test/packages.config +++ b/tests/Medidata.ZipkinTracer.Core.Test/packages.config @@ -2,7 +2,6 @@ - From c61260c03ba584a88061afa243c479e1cc26d3e6 Mon Sep 17 00:00:00 2001 From: Peter Mounce Date: Thu, 30 Jun 2016 07:31:04 +0100 Subject: [PATCH 2/3] Mention LibLog in the readme * Remove the logger from the code sample as it's no longer necessary - loggers are created within the library at need via the LibLog LogProvider, per the LibLog Library example (https://github.com/damianh/LibLog/blob/master/src/LibLog.Example.Library/Foo.cs) * Add a troubleshooting section mentioning LibLog and the initialisation caveat --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fef0f89..7a12f5e 100644 --- a/README.md +++ b/README.md @@ -76,18 +76,17 @@ public class Startup ### Client trace (Outbound request) Client Trace relies on HttpMessageHandler for HttpClient. Please pass a ZipkinMessageHandler instance into HttpClient. +Note: You will need the `GetOwinContext` extension method. If you host in IIS with `System.Web`, this can be found in `Microsoft.Owin.Host.SystemWeb`. ``` using Medidata.ZipkinTracer.Core.Handlers; public class HomeController : AsyncController { - private ILog logger = LogManager.GetLogger("HomeController"); - public async Task Index() { var context = System.Web.HttpContext.Current.GetOwinContext(); - var client = new ZipkinClient(logger, context); + var client = new ZipkinClient(context); using (var httpClient = new HttpClient(new ZipkinMessageHandler(client)))) { @@ -138,6 +137,12 @@ case the caller member name (method, property etc.) will get recorded. With the `RecordLocalComponent()` method of the client a local component (or information) can be recorded for the current trace. This will result an additional binary annotation with the 'lc' key (LOCAL_COMPONENT) and a custom value. +#### Troubleshooting + +##### Logs + +Logging internal to the library is provided via the [LibLog abstraction](https://github.com/damianh/LibLog). Caveat: to get logs, you must have initialised your logging framework on application-start ([console app example](https://github.com/damianh/LibLog/blob/master/src/LibLog.Example.Log4Net/Program.cs#L12) - a web-app might do this in OWIN Startup or Global.asax, or the inversion of control container initialisation). + ## Contributors ZipkinTracer is (c) Medidata Solutions Worldwide and owned by its major contributors: * Tomoko Kwan From 4dc576dd6554034876efbaf4f3d9b68f191f5ad6 Mon Sep 17 00:00:00 2001 From: Peter Mounce Date: Thu, 30 Jun 2016 17:02:56 +0100 Subject: [PATCH 3/3] unnecessary using --- src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs b/src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs index 1340119..aa34504 100644 --- a/src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs +++ b/src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Medidata.ZipkinTracer.Core.Logging; using Microsoft.Owin; using Owin;