diff --git a/sdk/src/Core/Amazon.Runtime/Internal/Util/Logger.cs b/sdk/src/Core/Amazon.Runtime/Internal/Util/Logger.cs index b50c525b6dd7..0b5b678bbeaa 100644 --- a/sdk/src/Core/Amazon.Runtime/Internal/Util/Logger.cs +++ b/sdk/src/Core/Amazon.Runtime/Internal/Util/Logger.cs @@ -13,14 +13,11 @@ * permissions and limitations under the License. */ using System; +using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; -using System.Globalization; -using System.Reflection; -using System.Text; using System.ComponentModel; -using Amazon.Runtime; +using System.Linq; +using System.Threading; using Amazon.Util.Internal; namespace Amazon.Runtime.Internal.Util @@ -31,9 +28,8 @@ namespace Amazon.Runtime.Internal.Util /// public class Logger : ILogger { - private static IDictionary cachedLoggers = new Dictionary(); + private static ConcurrentDictionary> cachedLoggers = new ConcurrentDictionary>(); private List loggers; - private static Logger emptyLogger = new Logger(); private Logger() { @@ -46,19 +42,20 @@ private Logger() #endif private Logger(Type type) { - loggers = new List(); - if(!InternalSDKUtils.IsRunningNativeAot()) { + loggers = new List(3); #pragma warning disable - InternalLog4netLogger log4netLogger = new InternalLog4netLogger(type); - loggers.Add(log4netLogger); + loggers.Add(new InternalLog4netLogger(type)); #pragma warning restore } + else + { + loggers = new List(2); + } loggers.Add(new InternalConsoleLogger(type)); - InternalSystemDiagnosticsLogger sdLogger = new InternalSystemDiagnosticsLogger(type); - loggers.Add(sdLogger); + loggers.Add(new InternalSystemDiagnosticsLogger(type)); ConfigureLoggers(); AWSConfigs.PropertyChanged += ConfigsChanged; } @@ -80,6 +77,23 @@ private void ConfigureLoggers() il.IsEnabled = (logging & LoggingOptions.Console) == LoggingOptions.Console; if (il is InternalSystemDiagnosticsLogger) il.IsEnabled = (logging & LoggingOptions.SystemDiagnostics) == LoggingOptions.SystemDiagnostics; + + if (!IsEnabled) + { + IsEnabled = il.IsEnabled; + } + if (!IsErrorEnabled) + { + IsErrorEnabled = il.IsErrorEnabled; + } + if (!IsInfoEnabled) + { + IsInfoEnabled = il.IsInfoEnabled; + } + if (!IsDebugEnabled) + { + IsDebugEnabled = il.IsDebugEnabled; + } } } @@ -87,32 +101,52 @@ private void ConfigureLoggers() public static Logger GetLogger(Type type) { - if (type == null) throw new ArgumentNullException("type"); + if (type == null) throw new ArgumentNullException(nameof(type)); - Logger l; - lock (cachedLoggers) - { - if (!cachedLoggers.TryGetValue(type, out l)) - { - l = new Logger(type); - cachedLoggers[type] = l; - } - } - return l; + // Use a lazy initialization to ensure that we only create a logger for a given type once because + // the constructor does some heavy lifting including setting up event listeners. + var lazyLogger = cachedLoggers.GetOrAdd(type, static t => new Lazy(() => new Logger(t))); + return lazyLogger.Value; } public static void ClearLoggerCache() { - lock (cachedLoggers) + var newLoggerCache = new ConcurrentDictionary>(); + ConcurrentDictionary> oldLoggerCache; + + do { - cachedLoggers = new Dictionary(); + oldLoggerCache = cachedLoggers; + } while (Interlocked.CompareExchange(ref cachedLoggers, newLoggerCache, oldLoggerCache) != oldLoggerCache); + + // Unregister all the loggers in the old cache + foreach (var item in oldLoggerCache ?? Enumerable.Empty>>()) + { + var lazyLogger = item.Value; + if (lazyLogger.IsValueCreated) + { + lazyLogger.Value.Unregister(); + } } } - public static Logger EmptyLogger { get { return emptyLogger; } } + public static Logger EmptyLogger { get; } = new Logger(); #endregion + internal bool IsEnabled { get; private set; } + + internal virtual bool IsErrorEnabled { get; private set; } + + internal virtual bool IsDebugEnabled { get; private set; } + + internal virtual bool IsInfoEnabled { get; private set; } + + internal void Unregister() + { + AWSConfigs.PropertyChanged -= ConfigsChanged; + } + #region Logging methods public void Flush() diff --git a/sdk/src/Core/Amazon.Util/AWSSDKUtils.cs b/sdk/src/Core/Amazon.Util/AWSSDKUtils.cs index 773ad7449c16..688452b85a43 100644 --- a/sdk/src/Core/Amazon.Util/AWSSDKUtils.cs +++ b/sdk/src/Core/Amazon.Util/AWSSDKUtils.cs @@ -385,11 +385,15 @@ public static string CanonicalizeResourcePathV2(Uri endpoint, string resourcePat string canonicalizedResourcePath = JoinResourcePathSegmentsV2(encodedSegments); // Get the logger each time (it's cached) because we shouldn't store it in a static variable. - Logger.GetLogger(typeof(AWSSDKUtils)).DebugFormat("{0} encoded {1}{2} for canonicalization: {3}", - pathWasPreEncoded ? "Double" : "Single", - resourcePath, - endpoint == null ? "" : " with endpoint " + endpoint.AbsoluteUri, - canonicalizedResourcePath); + var logger = Logger.GetLogger(typeof(AWSSDKUtils)); + if (logger.IsDebugEnabled) + { + logger.DebugFormat("{0} encoded {1}{2} for canonicalization: {3}", + pathWasPreEncoded ? "Double" : "Single", + resourcePath, + endpoint == null ? "" : $" with endpoint {endpoint.AbsoluteUri}", + canonicalizedResourcePath); + } return canonicalizedResourcePath; }