Skip to content

Commit 0a0d37a

Browse files
committed
1
1 parent c18433d commit 0a0d37a

File tree

11 files changed

+92
-67
lines changed

11 files changed

+92
-67
lines changed

src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentOptions.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5+
using Microsoft.Extensions.Logging;
56

67
namespace Microsoft.DotNet.Watch
78
{
@@ -12,16 +13,21 @@ internal enum TestFlags
1213
RunningAsTest = 1 << 0,
1314
MockBrowser = 1 << 1,
1415

16+
/// <summary>
17+
/// Elevates the logging level
18+
/// </summary>
19+
TraceLogging = 1 << 2,
20+
1521
/// <summary>
1622
/// Instead of using <see cref="Console.ReadKey()"/> to watch for Ctrl+C, Ctlr+R, and other keys, read from standard input.
1723
/// This allows tests to trigger key based events.
1824
/// </summary>
19-
ReadKeyFromStdin = 1 << 2,
25+
ReadKeyFromStdin = 1 << 3,
2026

2127
/// <summary>
2228
/// Redirects the output of the launched browser process to watch output.
2329
/// </summary>
24-
RedirectBrowserOutput = 1 << 3,
30+
RedirectBrowserOutput = 1 << 4,
2531
}
2632

2733
internal sealed record EnvironmentOptions(
@@ -35,6 +41,7 @@ internal sealed record EnvironmentOptions(
3541
bool SuppressBrowserRefresh = false,
3642
bool SuppressEmojis = false,
3743
bool RestartOnRudeEdit = false,
44+
LogLevel? CliLogLevel = null,
3845
string? AutoReloadWebSocketHostName = null,
3946
int? AutoReloadWebSocketPort = null,
4047
string? BrowserPath = null,
@@ -53,6 +60,7 @@ internal sealed record EnvironmentOptions(
5360
SuppressBrowserRefresh: EnvironmentVariables.SuppressBrowserRefresh,
5461
SuppressEmojis: EnvironmentVariables.SuppressEmojis,
5562
RestartOnRudeEdit: EnvironmentVariables.RestartOnRudeEdit,
63+
CliLogLevel: EnvironmentVariables.CliLogLevel,
5664
AutoReloadWebSocketHostName: EnvironmentVariables.AutoReloadWSHostName,
5765
AutoReloadWebSocketPort: EnvironmentVariables.AutoReloadWSPort,
5866
BrowserPath: EnvironmentVariables.BrowserPath,

src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentVariables.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using Microsoft.Extensions.Logging;
5+
46
namespace Microsoft.DotNet.Watch;
57

68
internal static class EnvironmentVariables
@@ -20,7 +22,19 @@ public static class Names
2022
public const string SuppressBrowserRefresh = "DOTNET_WATCH_SUPPRESS_BROWSER_REFRESH";
2123
}
2224

23-
public static bool VerboseCliOutput => ReadBool("DOTNET_CLI_CONTEXT_VERBOSE");
25+
public static LogLevel? CliLogLevel
26+
{
27+
get
28+
{
29+
var value = Environment.GetEnvironmentVariable("DOTNET_CLI_CONTEXT_VERBOSE");
30+
return string.Equals(value, "trace", StringComparison.OrdinalIgnoreCase)
31+
? LogLevel.Trace
32+
: ParseBool(value)
33+
? LogLevel.Debug
34+
: null;
35+
}
36+
}
37+
2438
public static bool IsPollingEnabled => ReadBool("DOTNET_USE_POLLING_FILE_WATCHER");
2539
public static bool SuppressEmojis => ReadBool("DOTNET_WATCH_SUPPRESS_EMOJIS");
2640
public static bool RestartOnRudeEdit => ReadBool("DOTNET_WATCH_RESTART_ON_RUDE_EDIT");
@@ -46,11 +60,14 @@ public static class Names
4660
public static string? BrowserPath => Environment.GetEnvironmentVariable("DOTNET_WATCH_BROWSER_PATH");
4761

4862
private static bool ReadBool(string variableName)
49-
=> Environment.GetEnvironmentVariable(variableName) is var value && (value == "1" || bool.TryParse(value, out var boolValue) && boolValue);
63+
=> ParseBool(Environment.GetEnvironmentVariable(variableName));
5064

5165
private static TimeSpan? ReadTimeSpan(string variableName)
5266
=> Environment.GetEnvironmentVariable(variableName) is var value && long.TryParse(value, out var intValue) && intValue >= 0 ? TimeSpan.FromMilliseconds(intValue) : null;
5367

5468
private static int? ReadInt(string variableName)
5569
=> Environment.GetEnvironmentVariable(variableName) is var value && int.TryParse(value, out var intValue) ? intValue : null;
70+
71+
private static bool ParseBool(string? value)
72+
=> value == "1" || bool.TryParse(value, out var boolValue) && boolValue;
5673
}

src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public async Task WatchAsync(CancellationToken shutdownCancellationToken)
107107
}
108108

109109
var projectMap = new ProjectNodeMap(evaluationResult.ProjectGraph, _context.Logger);
110-
compilationHandler = new CompilationHandler(_context.Logger, _context.ProcessRunner);
110+
compilationHandler = new CompilationHandler(_context.Logger, _context.ProcessRunner, _context.EnvironmentOptions);
111111
var scopedCssFileHandler = new ScopedCssFileHandler(_context.Logger, _context.BuildLogger, projectMap, _context.BrowserRefreshServerFactory, _context.Options, _context.EnvironmentOptions);
112112
var projectLauncher = new ProjectLauncher(_context, projectMap, compilationHandler, iteration);
113113
evaluationResult.ItemExclusions.Report(_context.Logger);

src/BuiltInTools/dotnet-watch/HotReload/IncrementalMSBuildWorkspace.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.CodeAnalysis.MSBuild;
1010
using Microsoft.CodeAnalysis.Text;
1111
using Microsoft.Extensions.Logging;
12+
using Windows.Win32.Security.Cryptography;
1213

1314
namespace Microsoft.DotNet.Watch;
1415

@@ -32,6 +33,7 @@ public IncrementalMSBuildWorkspace(ILogger logger)
3233
#pragma warning restore CS0618
3334

3435
_logger = logger;
36+
_environmentOptions = environmentOptions;
3537
}
3638

3739
public async Task UpdateProjectConeAsync(string rootProjectPath, CancellationToken cancellationToken)
@@ -230,16 +232,13 @@ private Task UpdateSolutionAsync(Solution newSolution, string operationDisplayNa
230232

231233
private async Task ReportSolutionFilesAsync(Solution solution, int updateId, string operationDisplayName, CancellationToken cancellationToken)
232234
{
233-
#if DEBUG
234-
_logger.LogDebug("Solution: {Path}", solution.FilePath);
235+
_logger.LogDebug("Solution after {Operation}: v{Version}", operationDisplayName, updateId);
235236

236-
if (!_logger.IsEnabled(LogLevel.Debug))
237+
if (!_logger.IsEnabled(LogLevel.Trace))
237238
{
238239
return;
239240
}
240241

241-
_logger.LogDebug("Solution after {Operation}: v{Version}", operationDisplayName, updateId);
242-
243242
foreach (var project in solution.Projects)
244243
{
245244
_logger.LogDebug(" Project: {Path}", project.FilePath);
@@ -265,8 +264,5 @@ async ValueTask InspectDocumentAsync(TextDocument document, string kind)
265264
var text = await document.GetTextAsync(cancellationToken);
266265
_logger.LogDebug(" {Kind}: {FilePath} [{Checksum}]", kind, document.FilePath, Convert.ToBase64String(text.GetChecksum().ToArray()));
267266
}
268-
#else
269-
await Task.CompletedTask;
270-
#endif
271267
}
272268
}

src/BuiltInTools/dotnet-watch/Program.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ public static async Task<int> Main(string[] args)
4646
args,
4747
new PhysicalConsole(environmentOptions.TestFlags),
4848
environmentOptions,
49-
EnvironmentVariables.VerboseCliOutput,
5049
out var exitCode);
5150

5251
if (program == null)
@@ -64,18 +63,25 @@ public static async Task<int> Main(string[] args)
6463
}
6564
}
6665

67-
private static Program? TryCreate(IReadOnlyList<string> args, IConsole console, EnvironmentOptions environmentOptions, bool verbose, out int errorCode)
66+
private static Program? TryCreate(IReadOnlyList<string> args, IConsole console, EnvironmentOptions environmentOptions, out int errorCode)
6867
{
69-
var parsingLoggerFactory = new LoggerFactory(new ConsoleReporter(console, verbose, quiet: false, environmentOptions.SuppressEmojis));
68+
var parsingLoggerFactory = new LoggerFactory(new ConsoleReporter(console, environmentOptions.SuppressEmojis), LogLevel: environmentOptions.CliLogLevel ?? LogLevel.Information);
7069
var options = CommandLineOptions.Parse(args, parsingLoggerFactory.CreateLogger(LogComponentName), console.Out, out errorCode);
7170
if (options == null)
7271
{
7372
// an error reported or help printed:
7473
return null;
7574
}
7675

77-
var reporter = new ConsoleReporter(console, verbose || options.GlobalOptions.Verbose, options.GlobalOptions.Quiet, environmentOptions.SuppressEmojis);
78-
var loggerFactory = new LoggerFactory(reporter);
76+
var logLevel = environmentOptions.CliLogLevel ??
77+
(options.GlobalOptions.Verbose
78+
? LogLevel.Debug
79+
: options.GlobalOptions.Quiet
80+
? LogLevel.Warning
81+
: LogLevel.Information);
82+
83+
var reporter = new ConsoleReporter(console, environmentOptions.SuppressEmojis);
84+
var loggerFactory = new LoggerFactory(reporter, logLevel);
7985
return TryCreate(options, console, environmentOptions, loggerFactory, reporter, out errorCode);
8086
}
8187

src/BuiltInTools/dotnet-watch/UI/ConsoleReporter.cs

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ namespace Microsoft.DotNet.Watch
99
/// This API supports infrastructure and is not intended to be used
1010
/// directly from your code. This API may change or be removed in future releases.
1111
/// </summary>
12-
internal sealed class ConsoleReporter(IConsole console, bool verbose, bool quiet, bool suppressEmojis) : IReporter, IProcessOutputReporter
12+
internal sealed class ConsoleReporter(IConsole console, bool suppressEmojis) : IReporter, IProcessOutputReporter
1313
{
14-
public bool IsVerbose { get; } = verbose;
15-
public bool IsQuiet { get; } = quiet;
1614
public bool SuppressEmojis { get; } = suppressEmojis;
1715

1816
private readonly Lock _writeLock = new();
@@ -50,33 +48,18 @@ private void WriteLine(TextWriter writer, string message, ConsoleColor? color, E
5048
}
5149
}
5250

53-
public void Report(EventId id, Emoji emoji, MessageSeverity severity, string message)
51+
public void Report(EventId id, Emoji emoji, LogLevel level, string message)
5452
{
55-
switch (severity)
53+
var color = level switch
5654
{
57-
case MessageSeverity.Error:
58-
// Use stdout for error messages to preserve ordering with respect to other output.
59-
WriteLine(console.Error, message, ConsoleColor.Red, emoji);
60-
break;
55+
LogLevel.Critical or LogLevel.Error => ConsoleColor.Red,
56+
LogLevel.Warning => ConsoleColor.Yellow,
57+
LogLevel.Information => (ConsoleColor?)null,
58+
_ => ConsoleColor.DarkGray,
59+
};
6160

62-
case MessageSeverity.Warning:
63-
WriteLine(console.Error, message, ConsoleColor.Yellow, emoji);
64-
break;
65-
66-
case MessageSeverity.Output:
67-
if (!IsQuiet)
68-
{
69-
WriteLine(console.Error, message, color: null, emoji);
70-
}
71-
break;
72-
73-
case MessageSeverity.Verbose:
74-
if (IsVerbose)
75-
{
76-
WriteLine(console.Error, message, ConsoleColor.DarkGray, emoji);
77-
}
78-
break;
79-
}
61+
// Use stdout for error messages to preserve ordering with respect to other output.
62+
WriteLine(console.Error, message, color, emoji);
8063
}
8164
}
8265
}

src/BuiltInTools/dotnet-watch/UI/IReporter.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,31 +96,35 @@ public static MessageSeverity ToSeverity(this LogLevel level)
9696
};
9797
}
9898

99-
internal sealed class LoggerFactory(IReporter reporter) : ILoggerFactory
99+
internal sealed class LoggerFactory(IReporter reporter, LogLevel level) : ILoggerFactory
100100
{
101-
private sealed class Logger(IReporter reporter, string categoryName) : ILogger
101+
private sealed class Logger(IReporter reporter, LogLevel level, string categoryName) : ILogger
102102
{
103103
public bool IsEnabled(LogLevel logLevel)
104-
=> reporter.IsVerbose || logLevel > LogLevel.Debug;
104+
=> logLevel >= level;
105105

106106
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
107107
{
108+
if (!IsEnabled(logLevel))
109+
{
110+
return;
111+
}
112+
108113
var (name, display) = LoggingUtilities.ParseCategoryName(categoryName);
109114
var prefix = display != null ? $"[{display}] " : "";
110115

111-
var severity = logLevel.ToSeverity();
112116
var descriptor = eventId.Id != 0 ? MessageDescriptor.GetDescriptor(eventId) : default;
113117

114-
var emoji = severity switch
118+
var emoji = logLevel switch
115119
{
116120
_ when descriptor.Emoji != Emoji.Default => descriptor.Emoji,
117-
MessageSeverity.Error => Emoji.Error,
118-
MessageSeverity.Warning => Emoji.Warning,
121+
LogLevel.Error => Emoji.Error,
122+
LogLevel.Warning => Emoji.Warning,
119123
_ when MessageDescriptor.ComponentEmojis.TryGetValue(name, out var componentEmoji) => componentEmoji,
120124
_ => Emoji.Watch
121125
};
122126

123-
reporter.Report(eventId, emoji, severity, prefix + formatter(state, exception));
127+
reporter.Report(eventId, emoji, logLevel, prefix + formatter(state, exception));
124128
}
125129

126130
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
@@ -132,7 +136,7 @@ public void Dispose()
132136
}
133137

134138
public ILogger CreateLogger(string categoryName)
135-
=> new Logger(reporter, categoryName);
139+
=> new Logger(reporter, level, categoryName);
136140

137141
public void AddProvider(ILoggerProvider provider)
138142
=> throw new NotImplementedException();
@@ -256,7 +260,6 @@ public MessageDescriptor WithSeverityWhen(MessageSeverity severity, bool conditi
256260
public static readonly MessageDescriptor Building = Create("Building {0} ...", Emoji.Default, MessageSeverity.Output);
257261
public static readonly MessageDescriptor BuildSucceeded = Create("Build succeeded: {0}", Emoji.Default, MessageSeverity.Output);
258262
public static readonly MessageDescriptor BuildFailed = Create("Build failed: {0}", Emoji.Default, MessageSeverity.Output);
259-
260263
}
261264

262265
internal interface IProcessOutputReporter
@@ -278,9 +281,6 @@ internal interface IProcessOutputReporter
278281

279282
internal interface IReporter
280283
{
281-
void Report(EventId id, Emoji emoji, MessageSeverity severity, string message);
282-
283-
public bool IsVerbose
284-
=> false;
284+
void Report(EventId id, Emoji emoji, LogLevel level, string message);
285285
}
286286
}

test/dotnet-watch.Tests/CommandLine/ProgramTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#nullable disable
55

6+
using Microsoft.Extensions.Logging;
7+
68
namespace Microsoft.DotNet.Watch.UnitTests
79
{
810
public class ProgramTests(ITestOutputHelper logger) : DotNetWatchTestBase(logger)
@@ -15,7 +17,7 @@ public async Task ConsoleCancelKey()
1517

1618
var console = new TestConsole(Logger);
1719
var reporter = new TestReporter(Logger);
18-
var loggerFactory = new LoggerFactory(reporter);
20+
var loggerFactory = new LoggerFactory(reporter, LogLevel.Debug);
1921

2022
var watching = reporter.RegisterSemaphore(MessageDescriptor.WatchingWithHotReload);
2123
var shutdownRequested = reporter.RegisterSemaphore(MessageDescriptor.ShutdownRequested);

test/dotnet-watch.Tests/ConsoleReporterTests.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#nullable disable
55

6+
using Microsoft.Extensions.Logging;
7+
68
namespace Microsoft.DotNet.Watch.UnitTests
79
{
810
public class ConsoleReporterTests
@@ -15,23 +17,31 @@ public class ConsoleReporterTests
1517
public void WritesToStandardStreams(bool suppressEmojis)
1618
{
1719
var testConsole = new TestConsole();
18-
var reporter = new ConsoleReporter(testConsole, verbose: true, quiet: false, suppressEmojis: suppressEmojis);
20+
var reporter = new ConsoleReporter(testConsole, LogLevel.Debug, suppressEmojis: suppressEmojis);
21+
22+
reporter.Report(id: default, Emoji.Watch, LogLevel.Trace, "trace {0}");
23+
Assert.Empty(testConsole.GetError());
24+
testConsole.Clear();
1925

20-
reporter.Report(id: default, Emoji.Watch, MessageSeverity.Verbose, "verbose {0}");
26+
reporter.Report(id: default, Emoji.Watch, LogLevel.Debug, "verbose {0}");
2127
Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "⌚")} verbose {{0}}" + EOL, testConsole.GetError());
2228
testConsole.Clear();
2329

24-
reporter.Report(id: default, Emoji.Watch, MessageSeverity.Output, "out");
30+
reporter.Report(id: default, Emoji.Watch, LogLevel.Information, "out");
2531
Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "⌚")} out" + EOL, testConsole.GetError());
2632
testConsole.Clear();
2733

28-
reporter.Report(id: default, Emoji.Warning, MessageSeverity.Warning, "warn");
34+
reporter.Report(id: default, Emoji.Warning, LogLevel.Warning, "warn");
2935
Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "⚠")} warn" + EOL, testConsole.GetError());
3036
testConsole.Clear();
3137

32-
reporter.Report(id: default, Emoji.Error, MessageSeverity.Error, "error");
38+
reporter.Report(id: default, Emoji.Error, LogLevel.Error, "error");
3339
Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "❌")} error" + EOL, testConsole.GetError());
3440
testConsole.Clear();
41+
42+
reporter.Report(id: default, Emoji.Error, LogLevel.Critical, "critical");
43+
Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "❌")} critical" + EOL, testConsole.GetError());
44+
testConsole.Clear();
3545
}
3646

3747
private class TestConsole : IConsole

test/dotnet-watch.Tests/HotReload/CompilationHandlerTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using Microsoft.Extensions.Logging;
5+
46
namespace Microsoft.DotNet.Watch.UnitTests;
57

68
public class CompilationHandlerTests(ITestOutputHelper output) : DotNetWatchTestBase(output)
@@ -22,7 +24,7 @@ public async Task ReferenceOutputAssembly_False()
2224
var processRunner = new ProcessRunner(processCleanupTimeout: TimeSpan.Zero);
2325

2426
var reporter = new TestReporter(Logger);
25-
var loggerFactory = new LoggerFactory(reporter);
27+
var loggerFactory = new LoggerFactory(reporter, LogLevel.Debug);
2628
var logger = loggerFactory.CreateLogger("Test");
2729
var projectGraph = ProjectGraphUtilities.TryLoadProjectGraph(options.ProjectPath, globalOptions: [], logger, projectGraphRequired: false, CancellationToken.None);
2830
var handler = new CompilationHandler(logger, processRunner);

0 commit comments

Comments
 (0)