diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/Logger.Scope.cs b/libraries/src/AWS.Lambda.Powertools.Logging/Logger.Scope.cs index ae7f4050..0ef8cdb7 100644 --- a/libraries/src/AWS.Lambda.Powertools.Logging/Logger.Scope.cs +++ b/libraries/src/AWS.Lambda.Powertools.Logging/Logger.Scope.cs @@ -14,20 +14,21 @@ public static partial class Logger /// Thread-safe dictionary for per-thread scope storage. /// Uses ManagedThreadId as key to ensure isolation when Lambda processes /// multiple concurrent requests (AWS_LAMBDA_MAX_CONCURRENCY > 1). + /// Inner dictionary is ConcurrentDictionary for thread-safe operations. /// - private static readonly ConcurrentDictionary> _threadScopes = new(); + private static readonly ConcurrentDictionary> _threadScopes = new(); /// /// Gets the scope for the current thread. /// Creates a new dictionary if one doesn't exist for this thread. /// /// The scope. - private static IDictionary Scope + private static ConcurrentDictionary Scope { get { var threadId = Environment.CurrentManagedThreadId; - return _threadScopes.GetOrAdd(threadId, _ => new Dictionary(StringComparer.Ordinal)); + return _threadScopes.GetOrAdd(threadId, _ => new ConcurrentDictionary(StringComparer.Ordinal)); } } @@ -58,9 +59,10 @@ public static void AppendKey(string key, object value) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - - Scope[key] = PowertoolsLoggerHelpers.ObjectToDictionary(value) ?? + + var convertedValue = PowertoolsLoggerHelpers.ObjectToDictionary(value) ?? throw new ArgumentNullException(nameof(value)); + Scope[key] = convertedValue; } /// @@ -91,17 +93,18 @@ public static void RemoveKeys(params string[] keys) { if (keys == null) return; foreach (var key in keys) - if (Scope.ContainsKey(key)) - Scope.Remove(key); + Scope.TryRemove(key, out _); } /// /// Returns all additional keys added to the log context. + /// Returns a snapshot to ensure thread-safety during enumeration. /// /// IEnumerable<KeyValuePair<System.String, System.Object>>. public static IEnumerable> GetAllKeys() { - return Scope.AsEnumerable(); + // Return a snapshot to avoid concurrent modification issues + return Scope.ToArray(); } /// @@ -121,7 +124,6 @@ internal static void RemoveAllKeys() /// public static void RemoveKey(string key) { - if (Scope.ContainsKey(key)) - Scope.Remove(key); + Scope.TryRemove(key, out _); } }