From 81b60c366f0a78141f61713355c9581ce2b86e91 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Mon, 6 Oct 2025 13:53:43 -0700 Subject: [PATCH] Only enable ASP.NET debug logging if logging level is Trace --- .../HotReloadClient/Logging/LogEvents.cs | 1 + .../Web/AbstractBrowserRefreshServer.cs | 8 ++- .../dotnet-watch/Process/ProjectLauncher.cs | 2 +- .../Browser/BrowserRefreshServerTests.cs | 59 +++++++++++++++++++ .../TestUtilities/TestBrowserRefreshServer.cs | 17 ++++++ .../TestUtilities/TestLogger.cs | 4 +- .../TestUtilities/TestLoggerFactory.cs | 17 ++++++ 7 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 test/dotnet-watch.Tests/Browser/BrowserRefreshServerTests.cs create mode 100644 test/dotnet-watch.Tests/TestUtilities/TestBrowserRefreshServer.cs create mode 100644 test/dotnet-watch.Tests/TestUtilities/TestLoggerFactory.cs diff --git a/src/BuiltInTools/HotReloadClient/Logging/LogEvents.cs b/src/BuiltInTools/HotReloadClient/Logging/LogEvents.cs index f1672112866f..7be92781e01c 100644 --- a/src/BuiltInTools/HotReloadClient/Logging/LogEvents.cs +++ b/src/BuiltInTools/HotReloadClient/Logging/LogEvents.cs @@ -25,6 +25,7 @@ public static void Log(this ILogger logger, LogEvent logEvent, params object[] a public static readonly LogEvent HotReloadSucceeded = Create(LogLevel.Information, "Hot reload succeeded."); public static readonly LogEvent RefreshingBrowser = Create(LogLevel.Debug, "Refreshing browser."); public static readonly LogEvent ReloadingBrowser = Create(LogLevel.Debug, "Reloading browser."); + public static readonly LogEvent SendingWaitMessage = Create(LogLevel.Debug, "Sending wait message."); public static readonly LogEvent NoBrowserConnected = Create(LogLevel.Debug, "No browser is connected."); public static readonly LogEvent FailedToReceiveResponseFromConnectedBrowser = Create(LogLevel.Debug, "Failed to receive response from a connected browser."); public static readonly LogEvent UpdatingDiagnostics = Create(LogLevel.Debug, "Updating diagnostics."); diff --git a/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs b/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs index 9c313bb02b50..91d99296db0b 100644 --- a/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs +++ b/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs @@ -101,7 +101,7 @@ public void ConfigureLaunchEnvironment(IDictionary builder, bool builder[MiddlewareEnvironmentVariables.DotNetModifiableAssemblies] = "debug"; } - if (logger.IsEnabled(LogLevel.Debug)) + if (logger.IsEnabled(LogLevel.Trace)) { // enable debug logging from middleware: builder[MiddlewareEnvironmentVariables.LoggingLevel] = "Debug"; @@ -237,8 +237,12 @@ public ValueTask SendReloadMessageAsync(CancellationToken cancellationToken) } public ValueTask SendWaitMessageAsync(CancellationToken cancellationToken) - => SendAsync(s_waitMessage, cancellationToken); + { + logger.Log(LogEvents.SendingWaitMessage); + return SendAsync(s_waitMessage, cancellationToken); + } + // obsolete: to be removed public ValueTask SendPingMessageAsync(CancellationToken cancellationToken) => SendAsync(s_pingMessage, cancellationToken); diff --git a/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs b/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs index 475f7e2fe1d5..fb74333ebdd3 100644 --- a/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs +++ b/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs @@ -91,7 +91,7 @@ public EnvironmentOptions EnvironmentOptions environmentBuilder[EnvironmentVariables.Names.DotnetWatch] = "1"; environmentBuilder[EnvironmentVariables.Names.DotnetWatchIteration] = (Iteration + 1).ToString(CultureInfo.InvariantCulture); - if (Logger.IsEnabled(LogLevel.Debug)) + if (Logger.IsEnabled(LogLevel.Trace)) { environmentBuilder[EnvironmentVariables.Names.HotReloadDeltaClientLogMessages] = (EnvironmentOptions.SuppressEmojis ? Emoji.Default : Emoji.Agent).GetLogMessagePrefix() + $"[{projectDisplayName}]"; diff --git a/test/dotnet-watch.Tests/Browser/BrowserRefreshServerTests.cs b/test/dotnet-watch.Tests/Browser/BrowserRefreshServerTests.cs new file mode 100644 index 000000000000..236c99ec077d --- /dev/null +++ b/test/dotnet-watch.Tests/Browser/BrowserRefreshServerTests.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.HotReload; +using Microsoft.Extensions.Logging; + +namespace Microsoft.DotNet.Watch.UnitTests; + +public class BrowserRefreshServerTests +{ + class TestListener : IDisposable + { + public void Dispose() + { + } + } + + [Theory] + [CombinatorialData] + public async Task ConfigureLaunchEnvironmentAsync(LogLevel logLevel, bool enableHotReload) + { + var middlewarePath = Path.GetTempPath(); + var middlewareFileName = Path.GetFileNameWithoutExtension(middlewarePath); + + var server = new TestBrowserRefreshServer(middlewarePath) + { + CreateAndStartHostImpl = () => new WebServerHost(new TestListener(), ["http://test.endpoint"], virtualDirectory: "/test/virt/dir") + }; + + ((TestLogger)server.Logger).IsEnabledImpl = level => level == logLevel; + + await server.StartAsync(CancellationToken.None); + + var envBuilder = new Dictionary(); + server.ConfigureLaunchEnvironment(envBuilder, enableHotReload); + + Assert.True(envBuilder.Remove("ASPNETCORE_AUTO_RELOAD_WS_KEY")); + + var expected = new List() + { + "ASPNETCORE_AUTO_RELOAD_VDIR=/test/virt/dir", + "ASPNETCORE_AUTO_RELOAD_WS_ENDPOINT=http://test.endpoint", + "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=" + middlewareFileName, + "DOTNET_STARTUP_HOOKS=" + middlewarePath, + }; + + if (enableHotReload) + { + expected.Add("DOTNET_MODIFIABLE_ASSEMBLIES=debug"); + } + + if (logLevel == LogLevel.Trace) + { + expected.Add("Logging__LogLevel__Microsoft.AspNetCore.Watch=Debug"); + } + + AssertEx.SequenceEqual(expected.Order(), envBuilder.OrderBy(e => e.Key).Select(e => $"{e.Key}={e.Value}")); + } +} diff --git a/test/dotnet-watch.Tests/TestUtilities/TestBrowserRefreshServer.cs b/test/dotnet-watch.Tests/TestUtilities/TestBrowserRefreshServer.cs new file mode 100644 index 000000000000..06acc9719b39 --- /dev/null +++ b/test/dotnet-watch.Tests/TestUtilities/TestBrowserRefreshServer.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.HotReload; + +namespace Microsoft.DotNet.Watch.UnitTests; + +internal class TestBrowserRefreshServer(string middlewareAssemblyPath) + : AbstractBrowserRefreshServer(middlewareAssemblyPath, new TestLogger(), new TestLoggerFactory()) +{ + public Func? CreateAndStartHostImpl; + + protected override ValueTask CreateAndStartHostAsync(CancellationToken cancellationToken) + => ValueTask.FromResult((CreateAndStartHostImpl ?? throw new NotImplementedException())()); + + protected override bool SuppressTimeouts => true; +} diff --git a/test/dotnet-watch.Tests/TestUtilities/TestLogger.cs b/test/dotnet-watch.Tests/TestUtilities/TestLogger.cs index 3a9add553aa0..5ae8e7dcd811 100644 --- a/test/dotnet-watch.Tests/TestUtilities/TestLogger.cs +++ b/test/dotnet-watch.Tests/TestUtilities/TestLogger.cs @@ -11,6 +11,8 @@ internal class TestLogger(ITestOutputHelper? output = null) : ILogger public readonly Lock Guard = new(); private readonly List _messages = []; + public Func? IsEnabledImpl; + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { var message = $"[{logLevel}] {formatter(state, exception)}"; @@ -36,5 +38,5 @@ public ImmutableArray GetAndClearMessages() where TState : notnull => throw new NotImplementedException(); public bool IsEnabled(LogLevel logLevel) - => true; + => IsEnabledImpl?.Invoke(logLevel) ?? true; } diff --git a/test/dotnet-watch.Tests/TestUtilities/TestLoggerFactory.cs b/test/dotnet-watch.Tests/TestUtilities/TestLoggerFactory.cs new file mode 100644 index 000000000000..be1e69682952 --- /dev/null +++ b/test/dotnet-watch.Tests/TestUtilities/TestLoggerFactory.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Logging; + +namespace Microsoft.DotNet.Watch.UnitTests; + +internal class TestLoggerFactory(ITestOutputHelper? output = null) : ILoggerFactory +{ + public Func? CreateLoggerImpl; + + public ILogger CreateLogger(string categoryName) + => CreateLoggerImpl?.Invoke(categoryName) ?? new TestLogger(output); + + public void AddProvider(ILoggerProvider provider) {} + public void Dispose() { } +}