Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,20 @@ internal sealed class CommandLineOptions

var targetFrameworkOption = (Option<string>?)buildOptions.SingleOrDefault(option => option.Name == "--framework");

var logLevel = parseResult.GetValue(verboseOption)
? LogLevel.Debug
: parseResult.GetValue(quietOption)
? LogLevel.Warning
: LogLevel.Information;

return new()
{
List = parseResult.GetValue(listOption),
GlobalOptions = new()
{
Quiet = parseResult.GetValue(quietOption),
LogLevel = logLevel,
NoHotReload = parseResult.GetValue(noHotReloadOption),
NonInteractive = parseResult.GetValue(NonInteractiveOption),
Verbose = parseResult.GetValue(verboseOption),
BinaryLogPath = ParseBinaryLogFilePath(binLogPath),
},

Expand Down
12 changes: 10 additions & 2 deletions src/BuiltInTools/dotnet-watch/CommandLine/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/dotnet-watch/CommandLine/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/dotnet-watch/CommandLine/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
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
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,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
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
}
}
10 changes: 5 additions & 5 deletions src/BuiltInTools/dotnet-watch/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public static async Task<int> Main(string[] args)
args,
new PhysicalConsole(environmentOptions.TestFlags),
environmentOptions,
EnvironmentVariables.VerboseCliOutput,
out var exitCode);

if (program == null)
Expand All @@ -64,18 +63,19 @@ public static async Task<int> Main(string[] args)
}
}

private static Program? TryCreate(IReadOnlyList<string> args, IConsole console, EnvironmentOptions environmentOptions, bool verbose, out int errorCode)
private static Program? TryCreate(IReadOnlyList<string> args, IConsole console, EnvironmentOptions environmentOptions, out int errorCode)
{
var parsingLoggerFactory = new LoggerFactory(new ConsoleReporter(console, verbose, quiet: false, environmentOptions.SuppressEmojis));
var parsingLoggerFactory = new LoggerFactory(new ConsoleReporter(console, environmentOptions.SuppressEmojis), environmentOptions.CliLogLevel ?? LogLevel.Information);
var options = CommandLineOptions.Parse(args, parsingLoggerFactory.CreateLogger(LogComponentName), console.Out, out errorCode);
if (options == null)
{
// an error reported or help printed:
return null;
}

var reporter = new ConsoleReporter(console, verbose || options.GlobalOptions.Verbose, options.GlobalOptions.Quiet, environmentOptions.SuppressEmojis);
var loggerFactory = new LoggerFactory(reporter);
var logLevel = environmentOptions.CliLogLevel ?? options.GlobalOptions.LogLevel;
var reporter = new ConsoleReporter(console, environmentOptions.SuppressEmojis);
var loggerFactory = new LoggerFactory(reporter, logLevel);
return TryCreate(options, console, environmentOptions, loggerFactory, reporter, out errorCode);
}

Expand Down
4 changes: 2 additions & 2 deletions src/BuiltInTools/dotnet-watch/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"profiles": {
"dotnet-watch": {
"commandName": "Project",
"commandLineArgs": "--verbose -bl",
"workingDirectory": "C:\\bugs\\9756\\aspire-watch-start-issue\\Aspire.AppHost",
"commandLineArgs": "",
"workingDirectory": "C:\\temp\\Razor",
"environmentVariables": {
"DOTNET_WATCH_DEBUG_SDK_DIRECTORY": "$(RepoRoot)artifacts\\bin\\redist\\$(Configuration)\\dotnet\\sdk\\$(Version)",
"DCP_IDE_REQUEST_TIMEOUT_SECONDS": "100000",
Expand Down
6 changes: 4 additions & 2 deletions src/BuiltInTools/dotnet-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/dotnet-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