diff --git a/src/Datadog.Tracer.IntegrationTests.Net45/Datadog.Tracer.IntegrationTests.Net45.csproj b/src/Datadog.Tracer.IntegrationTests.Net45/Datadog.Tracer.IntegrationTests.Net45.csproj
index ff7f37317a44..9049e204874f 100644
--- a/src/Datadog.Tracer.IntegrationTests.Net45/Datadog.Tracer.IntegrationTests.Net45.csproj
+++ b/src/Datadog.Tracer.IntegrationTests.Net45/Datadog.Tracer.IntegrationTests.Net45.csproj
@@ -85,5 +85,8 @@
Datadog.Tracer
+
+
+
\ No newline at end of file
diff --git a/src/Datadog.Tracer/AgentWriter.cs b/src/Datadog.Tracer/AgentWriter.cs
index 1e79379f9d17..c4ecd5a1d14c 100644
--- a/src/Datadog.Tracer/AgentWriter.cs
+++ b/src/Datadog.Tracer/AgentWriter.cs
@@ -1,4 +1,5 @@
-using System;
+using Datadog.Tracer.Logging;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -7,6 +8,8 @@ namespace Datadog.Tracer
{
internal class AgentWriter : IAgentWriter
{
+ private static ILog _log = LogProvider.For();
+
private readonly AgentWriterBuffer> _tracesBuffer = new AgentWriterBuffer>(1000);
private readonly AgentWriterBuffer _servicesBuffer = new AgentWriterBuffer(100);
private readonly IApi _api;
@@ -20,12 +23,20 @@ public AgentWriter(IApi api)
public void WriteServiceInfo(ServiceInfo serviceInfo)
{
- _servicesBuffer.Push(serviceInfo);
+ var success = _servicesBuffer.Push(serviceInfo);
+ if (!success)
+ {
+ _log.Debug("ServiceInfo buffer is full, dropping it.");
+ }
}
public void WriteTrace(List trace)
{
- _tracesBuffer.Push(trace);
+ var success = _tracesBuffer.Push(trace);
+ if (!success)
+ {
+ _log.Debug("Trace buffer is full, dropping it.");
+ }
}
public async Task FlushTracesTaskLoop()
@@ -48,9 +59,9 @@ public async Task FlushTracesTaskLoop()
await Task.WhenAll(services.Select(_api.SendServiceAsync));
}
}
- catch
+ catch(Exception ex)
{
- // TODO: log errors
+ _log.ErrorException("An unhandled error occurred during the flushing task", ex);
}
}
}
diff --git a/src/Datadog.Tracer/Api.cs b/src/Datadog.Tracer/Api.cs
index 50d7c7bb544c..9b017fc89c58 100644
--- a/src/Datadog.Tracer/Api.cs
+++ b/src/Datadog.Tracer/Api.cs
@@ -1,4 +1,5 @@
-using MsgPack.Serialization;
+using Datadog.Tracer.Logging;
+using MsgPack.Serialization;
using System;
using System.Collections.Generic;
using System.Net.Http;
@@ -10,6 +11,8 @@ namespace Datadog.Tracer
{
internal class Api : IApi
{
+ private static ILog _log = LogProvider.For();
+
private const string TracesPath = "/v0.3/traces";
private const string ServicesPath = "/v0.3/services";
private static SerializationContext _serializationContext;
@@ -61,9 +64,9 @@ private async Task SendAsync(T value, Uri endpoint)
var response = await _client.PostAsync(endpoint, content);
response.EnsureSuccessStatusCode();
}
- catch
+ catch(Exception ex)
{
- // TODO:bertrand log
+ _log.ErrorException("An error occured while sending traces to the agent at {Endpoint}", ex, endpoint);
}
}
diff --git a/src/Datadog.Tracer/Datadog.Tracer.csproj b/src/Datadog.Tracer/Datadog.Tracer.csproj
index 512264212287..2b1fc06d3013 100644
--- a/src/Datadog.Tracer/Datadog.Tracer.csproj
+++ b/src/Datadog.Tracer/Datadog.Tracer.csproj
@@ -9,4 +9,18 @@
+
+ LIBLOG_PORTABLE
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Datadog.Tracer/LibLog.cs b/src/Datadog.Tracer/LibLog.cs
new file mode 100644
index 000000000000..11f851c6ba32
--- /dev/null
+++ b/src/Datadog.Tracer/LibLog.cs
@@ -0,0 +1,2263 @@
+//===============================================================================
+// 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 = "Datadog.Tracer.Logging")]
+[assembly: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Datadog.Tracer.Logging.Logger.#Invoke(Datadog.Tracer.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 Datadog.Tracer.LibLog
+#else
+namespace Datadog.Tracer.Logging
+#endif
+{
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+#if LIBLOG_PROVIDERS_ONLY
+ using Datadog.Tracer.LibLog.LogProviders;
+#else
+ using Datadog.Tracer.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
+ 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.
+ /// 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, string fallbackTypeName = "System.Object")
+ {
+ // If the type passed in is null then fallback to the type name specified
+ return GetLogger(type != null ? type.FullName : fallbackTypeName);
+ }
+
+ ///
+ /// 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 (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 Datadog.Tracer.LibLog.LogProviders
+#else
+namespace Datadog.Tracer.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;
+
+ 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;
+ }
+
+ [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 (_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);
+ }
+ switch (logLevel)
+ {
+ case LogLevel.Debug:
+ if (_logger.IsDebugEnabled)
+ {
+ _logger.Debug(messageFunc());
+ return true;
+ }
+ break;
+ case LogLevel.Info:
+ if (_logger.IsInfoEnabled)
+ {
+ _logger.Info(messageFunc());
+ return true;
+ }
+ break;
+ case LogLevel.Warn:
+ if (_logger.IsWarnEnabled)
+ {
+ _logger.Warn(messageFunc());
+ return true;
+ }
+ break;
+ case LogLevel.Error:
+ if (_logger.IsErrorEnabled)
+ {
+ _logger.Error(messageFunc());
+ return true;
+ }
+ break;
+ case LogLevel.Fatal:
+ if (_logger.IsFatalEnabled)
+ {
+ _logger.Fatal(messageFunc());
+ return true;
+ }
+ break;
+ default:
+ if (_logger.IsTraceEnabled)
+ {
+ _logger.Trace(messageFunc());
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ private 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)
+ {
+ 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;
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+ }
+
+ 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