diff --git a/dotnet/src/webdriver/Internal/Logging/LogContext.cs b/dotnet/src/webdriver/Internal/Logging/LogContext.cs index bb4d9feede2c5..081ebbd4c5ab4 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogContext.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogContext.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; #nullable enable @@ -30,7 +31,7 @@ namespace OpenQA.Selenium.Internal.Logging /// Represents a logging context that provides methods for creating sub-contexts, retrieving loggers, emitting log messages, and configuring minimum log levels. /// /// - internal class LogContext : ILogContext + internal sealed class LogContext : ILogContext { private ConcurrentDictionary? _loggers; @@ -46,7 +47,7 @@ public LogContext(LogEventLevel level, ILogContext? parentLogContext, Concurrent _parentLogContext = parentLogContext; - _loggers = loggers; + _loggers = CloneLoggers(loggers, level); if (handlers is not null) { @@ -65,12 +66,7 @@ public ILogContext CreateContext() public ILogContext CreateContext(LogEventLevel minimumLevel) { - ConcurrentDictionary? loggers = null; - - if (_loggers != null) - { - loggers = new ConcurrentDictionary(_loggers.Select(l => new KeyValuePair(l.Key, new Logger(l.Value.Issuer, minimumLevel)))); - } + ConcurrentDictionary? loggers = CloneLoggers(_loggers, minimumLevel); var context = new LogContext(minimumLevel, this, loggers, Handlers); @@ -98,7 +94,7 @@ public ILogger GetLogger(Type type) public bool IsEnabled(ILogger logger, LogEventLevel level) { - return Handlers != null && level >= _level && level >= logger.Level; + return Handlers != null && level >= _level && (_loggers?.TryGetValue(logger.Issuer, out var loggerEntry) != true || level >= loggerEntry?.Level); } public void EmitMessage(ILogger logger, LogEventLevel level, string message) @@ -155,5 +151,24 @@ public void Dispose() Log.CurrentContext = _parentLogContext; } + + [return: NotNullIfNotNull(nameof(loggers))] + private static ConcurrentDictionary? CloneLoggers(ConcurrentDictionary? loggers, LogEventLevel minimumLevel) + { + if (loggers is null) + { + return null; + } + + var cloned = new Dictionary(loggers.Count); + + foreach (KeyValuePair logger in loggers) + { + var clonedLogger = new Logger(logger.Value.Issuer, minimumLevel); + cloned.Add(logger.Key, clonedLogger); + } + + return new ConcurrentDictionary(cloned); + } } } diff --git a/dotnet/test/common/Internal/Logging/LogTest.cs b/dotnet/test/common/Internal/Logging/LogTest.cs index d872f665c094a..a7d67684c2609 100644 --- a/dotnet/test/common/Internal/Logging/LogTest.cs +++ b/dotnet/test/common/Internal/Logging/LogTest.cs @@ -28,13 +28,27 @@ public class LogTest private TestLogHandler testLogHandler; private ILogger logger; + private void ResetGlobalLog() + { + Log.SetLevel(LogEventLevel.Info); + Log.Handlers.Clear().Handlers.Add(new ConsoleLogHandler()); + } + [SetUp] public void SetUp() { + ResetGlobalLog(); + testLogHandler = new TestLogHandler(); logger = Log.GetLogger(); } + [TearDown] + public void TearDown() + { + ResetGlobalLog(); + } + [Test] public void LoggerShouldEmitEvent() { @@ -160,6 +174,16 @@ public void ContextShouldChangeLevel() Assert.That(logger.Level, Is.EqualTo(LogEventLevel.Warn)); } + [Test] + public void ContextShouldEmitMessages() + { + using var context = Log.CreateContext(LogEventLevel.Trace).Handlers.Add(testLogHandler); + + logger.Trace("test message"); + + Assert.That(testLogHandler.Events.Count, Is.EqualTo(1)); + } + [Test] public void ShouldCreateContextWithCustomHandler() {