Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/BuiltInTools/Watch.Aspire/DotNetWatchLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ public static async Task<bool> RunAsync(string workingDirectory, DotNetWatchOpti
{
var globalOptions = new GlobalOptions()
{
Quiet = options.IsQuiet,
Verbose = options.IsVerbose,
LogLevel = options.LogLevel,
NoHotReload = false,
NonInteractive = true,
};
Expand Down Expand Up @@ -42,10 +41,10 @@ public static async Task<bool> RunAsync(string workingDirectory, DotNetWatchOpti
var muxerPath = Path.GetFullPath(Path.Combine(options.SdkDirectory, "..", "..", "dotnet" + PathUtilities.ExecutableExtension));

var console = new PhysicalConsole(TestFlags.None);
var reporter = new ConsoleReporter(console, globalOptions.Verbose, globalOptions.Quiet, suppressEmojis: false);
var reporter = new ConsoleReporter(console, suppressEmojis: false);
var environmentOptions = EnvironmentOptions.FromEnvironment(muxerPath);
var processRunner = new ProcessRunner(environmentOptions.GetProcessCleanupTimeout(isHotReloadEnabled: true));
var loggerFactory = new LoggerFactory(reporter);
var loggerFactory = new LoggerFactory(reporter, globalOptions.LogLevel);
var logger = loggerFactory.CreateLogger(DotNetWatchContext.DefaultLogComponentName);

using var context = new DotNetWatchContext()
Expand Down
7 changes: 3 additions & 4 deletions src/BuiltInTools/Watch.Aspire/DotNetWatchOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.CommandLine;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;

namespace Microsoft.DotNet.Watch;

Expand All @@ -17,8 +18,7 @@ internal sealed class DotNetWatchOptions

public required string ProjectPath { get; init; }
public required ImmutableArray<string> ApplicationArguments { get; init; }
public bool IsVerbose { get; init; }
public bool IsQuiet { get; init; }
public LogLevel LogLevel { get; init; }
public bool NoLaunchProfile { get; init; }

public static bool TryParse(string[] args, [NotNullWhen(true)] out DotNetWatchOptions? options)
Expand Down Expand Up @@ -71,8 +71,7 @@ public static bool TryParse(string[] args, [NotNullWhen(true)] out DotNetWatchOp
{
SdkDirectory = parseResult.GetRequiredValue(sdkOption),
ProjectPath = parseResult.GetRequiredValue(projectOption),
IsQuiet = parseResult.GetValue(quietOption),
IsVerbose = parseResult.GetValue(verboseOption),
LogLevel = parseResult.GetValue(quietOption) ? LogLevel.Warning : parseResult.GetValue(verboseOption) ? LogLevel.Debug : LogLevel.Information,
ApplicationArguments = [.. parseResult.GetValue(applicationArguments) ?? []],
NoLaunchProfile = parseResult.GetValue(noLaunchProfileOption),
};
Expand Down
4 changes: 2 additions & 2 deletions src/BuiltInTools/Watch/AppModels/WebApplicationAppModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ public bool IsServerSupported(ProjectGraphNode projectNode, ILogger logger)
{
if (context.EnvironmentOptions.SuppressBrowserRefresh)
{
logger.Log(MessageDescriptor.SkippingConfiguringBrowserRefresh_SuppressedViaEnvironmentVariable.WithSeverityWhen(MessageSeverity.Error, RequiresBrowserRefresh), EnvironmentVariables.Names.SuppressBrowserRefresh);
logger.Log(MessageDescriptor.SkippingConfiguringBrowserRefresh_SuppressedViaEnvironmentVariable.WithLevelWhen(LogLevel.Error, RequiresBrowserRefresh), EnvironmentVariables.Names.SuppressBrowserRefresh);
return false;
}

if (!projectNode.IsNetCoreApp(minVersion: s_minimumSupportedVersion))
{
logger.Log(MessageDescriptor.SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported.WithSeverityWhen(MessageSeverity.Error, RequiresBrowserRefresh));
logger.Log(MessageDescriptor.SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported.WithLevelWhen(LogLevel.Error, RequiresBrowserRefresh));
return false;
}

Expand Down
12 changes: 10 additions & 2 deletions src/BuiltInTools/Watch/Context/EnvironmentOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using Microsoft.Extensions.Logging;

namespace Microsoft.DotNet.Watch
{
Expand All @@ -12,16 +13,21 @@ internal enum TestFlags
RunningAsTest = 1 << 0,
MockBrowser = 1 << 1,

/// <summary>
/// Elevates the logging level
/// </summary>
TraceLogging = 1 << 2,

/// <summary>
/// Instead of using <see cref="Console.ReadKey()"/> to watch for Ctrl+C, Ctlr+R, and other keys, read from standard input.
/// This allows tests to trigger key based events.
/// </summary>
ReadKeyFromStdin = 1 << 2,
ReadKeyFromStdin = 1 << 3,

/// <summary>
/// Redirects the output of the launched browser process to watch output.
/// </summary>
RedirectBrowserOutput = 1 << 3,
RedirectBrowserOutput = 1 << 4,
}

internal sealed record EnvironmentOptions(
Expand All @@ -35,6 +41,7 @@ internal sealed record EnvironmentOptions(
bool SuppressBrowserRefresh = false,
bool SuppressEmojis = false,
bool RestartOnRudeEdit = false,
LogLevel? CliLogLevel = null,
string? AutoReloadWebSocketHostName = null,
int? AutoReloadWebSocketPort = null,
string? BrowserPath = null,
Expand All @@ -53,6 +60,7 @@ internal sealed record EnvironmentOptions(
SuppressBrowserRefresh: EnvironmentVariables.SuppressBrowserRefresh,
SuppressEmojis: EnvironmentVariables.SuppressEmojis,
RestartOnRudeEdit: EnvironmentVariables.RestartOnRudeEdit,
CliLogLevel: EnvironmentVariables.CliLogLevel,
AutoReloadWebSocketHostName: EnvironmentVariables.AutoReloadWSHostName,
AutoReloadWebSocketPort: EnvironmentVariables.AutoReloadWSPort,
BrowserPath: EnvironmentVariables.BrowserPath,
Expand Down
21 changes: 19 additions & 2 deletions src/BuiltInTools/Watch/Context/EnvironmentVariables.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// 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;

internal static class EnvironmentVariables
Expand All @@ -20,7 +22,19 @@ public static class Names
public const string SuppressBrowserRefresh = "DOTNET_WATCH_SUPPRESS_BROWSER_REFRESH";
}

public static bool VerboseCliOutput => ReadBool("DOTNET_CLI_CONTEXT_VERBOSE");
public static LogLevel? CliLogLevel
{
get
{
var value = Environment.GetEnvironmentVariable("DOTNET_CLI_CONTEXT_VERBOSE");
return string.Equals(value, "trace", StringComparison.OrdinalIgnoreCase)
? LogLevel.Trace
: ParseBool(value)
? LogLevel.Debug
: null;
}
}

public static bool IsPollingEnabled => ReadBool("DOTNET_USE_POLLING_FILE_WATCHER");
public static bool SuppressEmojis => ReadBool("DOTNET_WATCH_SUPPRESS_EMOJIS");
public static bool RestartOnRudeEdit => ReadBool("DOTNET_WATCH_RESTART_ON_RUDE_EDIT");
Expand All @@ -46,11 +60,14 @@ public static class Names
public static string? BrowserPath => Environment.GetEnvironmentVariable("DOTNET_WATCH_BROWSER_PATH");

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

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

private static int? ReadInt(string variableName)
=> Environment.GetEnvironmentVariable(variableName) is var value && int.TryParse(value, out var intValue) ? intValue : null;

private static bool ParseBool(string? value)
=> value == "1" || bool.TryParse(value, out var boolValue) && boolValue;
}
5 changes: 3 additions & 2 deletions src/BuiltInTools/Watch/Context/GlobalOptions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// 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;

internal sealed class GlobalOptions
{
public bool Quiet { get; init; }
public bool Verbose { get; init; }
public LogLevel LogLevel { get; init; }
public bool NoHotReload { get; init; }
public bool NonInteractive { get; init; }

Expand Down
2 changes: 1 addition & 1 deletion src/BuiltInTools/Watch/HotReload/CompilationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ void ReportDiagnostic(Diagnostic diagnostic, MessageDescriptor descriptor, strin
{
errorsToDisplayInApp.Add(MessageDescriptor.RestartingApplicationToApplyChanges.GetMessage());
}
else if (descriptor.Severity != MessageSeverity.None)
else if (descriptor.Level != LogLevel.None)
{
errorsToDisplayInApp.Add(descriptor.GetMessage(args));
}
Expand Down
2 changes: 1 addition & 1 deletion src/BuiltInTools/Watch/HotReload/HotReloadDotNetWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public HotReloadDotNetWatcher(DotNetWatchContext context, IConsole console, IRun
_runtimeProcessLauncherFactory = runtimeProcessLauncherFactory;
if (!context.Options.NonInteractive)
{
var consoleInput = new ConsoleInputReader(_console, context.Options.Quiet, context.EnvironmentOptions.SuppressEmojis);
var consoleInput = new ConsoleInputReader(_console, context.Options.LogLevel, context.EnvironmentOptions.SuppressEmojis);

var noPrompt = context.EnvironmentOptions.RestartOnRudeEdit;
if (noPrompt)
Expand Down
10 changes: 2 additions & 8 deletions src/BuiltInTools/Watch/HotReload/IncrementalMSBuildWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,16 +230,13 @@ private Task UpdateSolutionAsync(Solution newSolution, string operationDisplayNa

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

if (!_logger.IsEnabled(LogLevel.Debug))
if (!_logger.IsEnabled(LogLevel.Trace))
{
return;
}

_logger.LogDebug("Solution after {Operation}: v{Version}", operationDisplayName, updateId);

foreach (var project in solution.Projects)
{
_logger.LogDebug(" Project: {Path}", project.FilePath);
Expand All @@ -265,8 +262,5 @@ async ValueTask InspectDocumentAsync(TextDocument document, string kind)
var text = await document.GetTextAsync(cancellationToken);
_logger.LogDebug(" {Kind}: {FilePath} [{Checksum}]", kind, document.FilePath, Convert.ToBase64String(text.GetChecksum().ToArray()));
}
#else
await Task.CompletedTask;
#endif
}
}
6 changes: 4 additions & 2 deletions src/BuiltInTools/Watch/UI/ConsoleInputReader.cs
Original file line number Diff line number Diff line change
@@ -1,15 +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
{
internal sealed class ConsoleInputReader(IConsole console, bool quiet, bool suppressEmojis)
internal sealed class ConsoleInputReader(IConsole console, LogLevel logLevel, bool suppressEmojis)
{
private readonly object _writeLock = new();

public async Task<ConsoleKey> GetKeyAsync(string prompt, Func<ConsoleKeyInfo, bool> validateInput, CancellationToken cancellationToken)
{
if (quiet)
if (logLevel > LogLevel.Information)
{
return ConsoleKey.Escape;
}
Expand Down
37 changes: 10 additions & 27 deletions src/BuiltInTools/Watch/UI/ConsoleReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ namespace Microsoft.DotNet.Watch
/// This API supports infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
internal sealed class ConsoleReporter(IConsole console, bool verbose, bool quiet, bool suppressEmojis) : IReporter, IProcessOutputReporter
internal sealed class ConsoleReporter(IConsole console, bool suppressEmojis) : IReporter, IProcessOutputReporter
{
public bool IsVerbose { get; } = verbose;
public bool IsQuiet { get; } = quiet;
public bool SuppressEmojis { get; } = suppressEmojis;

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

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

case MessageSeverity.Warning:
WriteLine(console.Error, message, ConsoleColor.Yellow, emoji);
break;

case MessageSeverity.Output:
if (!IsQuiet)
{
WriteLine(console.Error, message, color: null, emoji);
}
break;

case MessageSeverity.Verbose:
if (IsVerbose)
{
WriteLine(console.Error, message, ConsoleColor.DarkGray, emoji);
}
break;
}
// Use stdout for error messages to preserve ordering with respect to other output.
WriteLine(console.Error, message, color, emoji);
}
}
}
Loading
Loading