diff --git a/.vsts-ci.yml b/.vsts-ci.yml
index af45421f086b..14937d384bfb 100644
--- a/.vsts-ci.yml
+++ b/.vsts-ci.yml
@@ -104,7 +104,7 @@ extends:
publishTaskPrefix: 1ES.
populateInternalRuntimeVariables: true
runtimeSourceProperties: /p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64)
- locBranch: release/10.0.1xx
+ locBranch: release/10.0.2xx
# WORKAROUND: BinSkim requires the folder exist prior to scanning.
preSteps:
- powershell: New-Item -ItemType Directory -Path $(Build.SourcesDirectory)/artifacts/bin -Force
diff --git a/Directory.Build.props b/Directory.Build.props
index 1101288f76a8..f079f24c8e12 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -12,7 +12,6 @@
-
true
@@ -75,9 +74,7 @@
- false
false
-
diff --git a/documentation/general/analyzer-redirecting.md b/documentation/general/analyzer-redirecting.md
index 993445af4ce6..45c59deab17d 100644
--- a/documentation/general/analyzer-redirecting.md
+++ b/documentation/general/analyzer-redirecting.md
@@ -48,11 +48,31 @@ And metadata at `metadata.json`:
```json
{
- "AspNetCoreAnalyzers": "9.0.0-preview.5.24306.11",
- "NetCoreAnalyzers": "9.0.0-preview.5.24306.7",
- "WindowsDesktopAnalyzers": "9.0.0-preview.5.24306.8",
- "SDKAnalyzers": "9.0.100-dev",
- "WebSDKAnalyzers": "9.0.100-dev",
+ "AspNetCoreAnalyzers":
+ {
+ "Version": "9.0.0-preview.5.24306.11",
+ "Files": ["analyzers\\dotnet\\cs\\Microsoft.AspNetCore.App.Analyzers.dll"]
+ },
+ "NetCoreAnalyzers":
+ {
+ "Version": "9.0.0-preview.5.24306.7",
+ "Files": ["analyzers\\dotnet\\cs\\System.Text.RegularExpressions.Generator.dll"]
+ },
+ "WindowsDesktopAnalyzers":
+ {
+ "Version": "9.0.0-preview.5.24306.8",
+ "Files": ["analyzers\\dotnet\\System.Windows.Forms.Analyzers.dll"]
+ },
+ "SDKAnalyzers":
+ {
+ "Version": "9.0.100-dev",
+ "Files": ["Sdks\\Microsoft.NET.Sdk\\analyzers\\Microsoft.CodeAnalysis.NetAnalyzers.dll"]
+ },
+ "WebSDKAnalyzers":
+ {
+ "Version": "9.0.100-dev",
+ "Files": ["Sdks\\Microsoft.NET.Sdk.Web\\analyzers\\cs\\Microsoft.AspNetCore.Analyzers.dll"]
+ },
}
```
@@ -73,7 +93,7 @@ will be redirected to
{InstallDir}\SDKAnalyzers\Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll
```
-where `metadata.json` has `"SDKAnalyzers": "9.0.100-dev"`, because
+where `metadata.json` has `"SDKAnalyzers": { "Version": "9.0.100-dev" }`, because
1. the suffix `Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll` matches, and
2. the version `9.0.100-preview.5.24307.3` has the same major and minor component (`9.0`) as the version `9.0.100-dev`
(both versions are read from the paths, not DLL metadata).
diff --git a/eng/Analyzers.props b/eng/Analyzers.props
deleted file mode 100644
index 170e51092bb9..000000000000
--- a/eng/Analyzers.props
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/eng/Versions.props b/eng/Versions.props
index 3cc2266df2a6..a640137af702 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -58,7 +58,6 @@
2.0.3
13.0.3
4.8.6
- 1.2.0-beta.435
4.0.5
2.0.0-beta5.25279.2
1.1.2
diff --git a/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs b/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs
index f64180144078..61af429cfd25 100644
--- a/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs
+++ b/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs
@@ -297,8 +297,12 @@ public void ApplyStaticAssetUpdate(RuntimeStaticAssetUpdate update)
///
public static void ClearHotReloadEnvironmentVariables(Type startupHookType)
{
- Environment.SetEnvironmentVariable(AgentEnvironmentVariables.DotNetStartupHooks,
- RemoveCurrentAssembly(startupHookType, Environment.GetEnvironmentVariable(AgentEnvironmentVariables.DotNetStartupHooks)!));
+ var startupHooks = Environment.GetEnvironmentVariable(AgentEnvironmentVariables.DotNetStartupHooks);
+ if (!string.IsNullOrEmpty(startupHooks))
+ {
+ Environment.SetEnvironmentVariable(AgentEnvironmentVariables.DotNetStartupHooks,
+ RemoveCurrentAssembly(startupHookType, startupHooks));
+ }
Environment.SetEnvironmentVariable(AgentEnvironmentVariables.DotNetWatchHotReloadNamedPipeName, null);
Environment.SetEnvironmentVariable(AgentEnvironmentVariables.HotReloadDeltaClientLogMessages, null);
@@ -307,10 +311,7 @@ public static void ClearHotReloadEnvironmentVariables(Type startupHookType)
// internal for testing
internal static string RemoveCurrentAssembly(Type startupHookType, string environment)
{
- if (environment is "")
- {
- return environment;
- }
+ Debug.Assert(!string.IsNullOrEmpty(environment), $"{nameof(environment)} must be set");
var comparison = Path.DirectorySeparatorChar == '\\' ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
diff --git a/src/BuiltInTools/Watch.Aspire/DotNetWatchLauncher.cs b/src/BuiltInTools/Watch.Aspire/DotNetWatchLauncher.cs
index 260b35c23042..6a796e464d79 100644
--- a/src/BuiltInTools/Watch.Aspire/DotNetWatchLauncher.cs
+++ b/src/BuiltInTools/Watch.Aspire/DotNetWatchLauncher.cs
@@ -11,8 +11,7 @@ public static async Task RunAsync(string workingDirectory, DotNetWatchOpti
{
var globalOptions = new GlobalOptions()
{
- Quiet = options.IsQuiet,
- Verbose = options.IsVerbose,
+ LogLevel = options.LogLevel,
NoHotReload = false,
NonInteractive = true,
};
@@ -42,10 +41,10 @@ public static async Task 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()
diff --git a/src/BuiltInTools/Watch.Aspire/DotNetWatchOptions.cs b/src/BuiltInTools/Watch.Aspire/DotNetWatchOptions.cs
index 2ccf84b885b5..1d8f526c1fff 100644
--- a/src/BuiltInTools/Watch.Aspire/DotNetWatchOptions.cs
+++ b/src/BuiltInTools/Watch.Aspire/DotNetWatchOptions.cs
@@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.CommandLine;
using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Logging;
namespace Microsoft.DotNet.Watch;
@@ -17,8 +18,7 @@ internal sealed class DotNetWatchOptions
public required string ProjectPath { get; init; }
public required ImmutableArray 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)
@@ -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),
};
diff --git a/src/BuiltInTools/Watch/AppModels/WebApplicationAppModel.cs b/src/BuiltInTools/Watch/AppModels/WebApplicationAppModel.cs
index cefcd43cf320..0f1fbb74d5d8 100644
--- a/src/BuiltInTools/Watch/AppModels/WebApplicationAppModel.cs
+++ b/src/BuiltInTools/Watch/AppModels/WebApplicationAppModel.cs
@@ -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;
}
diff --git a/src/BuiltInTools/Watch/Context/EnvironmentOptions.cs b/src/BuiltInTools/Watch/Context/EnvironmentOptions.cs
index 70c121ccf7c1..8eb705b17926 100644
--- a/src/BuiltInTools/Watch/Context/EnvironmentOptions.cs
+++ b/src/BuiltInTools/Watch/Context/EnvironmentOptions.cs
@@ -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
{
@@ -35,6 +36,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,
@@ -53,6 +55,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,
diff --git a/src/BuiltInTools/Watch/Context/EnvironmentVariables.cs b/src/BuiltInTools/Watch/Context/EnvironmentVariables.cs
index 28763d7f3223..41ac7d88edea 100644
--- a/src/BuiltInTools/Watch/Context/EnvironmentVariables.cs
+++ b/src/BuiltInTools/Watch/Context/EnvironmentVariables.cs
@@ -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
@@ -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");
@@ -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;
}
diff --git a/src/BuiltInTools/Watch/Context/GlobalOptions.cs b/src/BuiltInTools/Watch/Context/GlobalOptions.cs
index 0f24692c8522..2dc002f6070e 100644
--- a/src/BuiltInTools/Watch/Context/GlobalOptions.cs
+++ b/src/BuiltInTools/Watch/Context/GlobalOptions.cs
@@ -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; }
diff --git a/src/BuiltInTools/Watch/HotReload/CompilationHandler.cs b/src/BuiltInTools/Watch/HotReload/CompilationHandler.cs
index 2929d4f1e71e..e95ccc1be34f 100644
--- a/src/BuiltInTools/Watch/HotReload/CompilationHandler.cs
+++ b/src/BuiltInTools/Watch/HotReload/CompilationHandler.cs
@@ -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));
}
diff --git a/src/BuiltInTools/Watch/HotReload/HotReloadDotNetWatcher.cs b/src/BuiltInTools/Watch/HotReload/HotReloadDotNetWatcher.cs
index 5466d27b32c2..52df0fa62829 100644
--- a/src/BuiltInTools/Watch/HotReload/HotReloadDotNetWatcher.cs
+++ b/src/BuiltInTools/Watch/HotReload/HotReloadDotNetWatcher.cs
@@ -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)
diff --git a/src/BuiltInTools/Watch/HotReload/IncrementalMSBuildWorkspace.cs b/src/BuiltInTools/Watch/HotReload/IncrementalMSBuildWorkspace.cs
index 06b79772ddf6..2fc7e54a9b34 100644
--- a/src/BuiltInTools/Watch/HotReload/IncrementalMSBuildWorkspace.cs
+++ b/src/BuiltInTools/Watch/HotReload/IncrementalMSBuildWorkspace.cs
@@ -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);
@@ -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
}
}
diff --git a/src/BuiltInTools/Watch/UI/ConsoleInputReader.cs b/src/BuiltInTools/Watch/UI/ConsoleInputReader.cs
index 4c9e930b245a..233d320765ba 100644
--- a/src/BuiltInTools/Watch/UI/ConsoleInputReader.cs
+++ b/src/BuiltInTools/Watch/UI/ConsoleInputReader.cs
@@ -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 GetKeyAsync(string prompt, Func validateInput, CancellationToken cancellationToken)
{
- if (quiet)
+ if (logLevel > LogLevel.Information)
{
return ConsoleKey.Escape;
}
diff --git a/src/BuiltInTools/Watch/UI/ConsoleReporter.cs b/src/BuiltInTools/Watch/UI/ConsoleReporter.cs
index 198e518590bb..60b156142b08 100644
--- a/src/BuiltInTools/Watch/UI/ConsoleReporter.cs
+++ b/src/BuiltInTools/Watch/UI/ConsoleReporter.cs
@@ -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.
///
- 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();
@@ -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);
}
}
}
diff --git a/src/BuiltInTools/Watch/UI/IReporter.cs b/src/BuiltInTools/Watch/UI/IReporter.cs
index 2a118db336ee..82b60996e1ab 100644
--- a/src/BuiltInTools/Watch/UI/IReporter.cs
+++ b/src/BuiltInTools/Watch/UI/IReporter.cs
@@ -2,21 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Immutable;
-using System.Diagnostics;
using Microsoft.DotNet.HotReload;
using Microsoft.Extensions.Logging;
namespace Microsoft.DotNet.Watch
{
- internal enum MessageSeverity
- {
- None,
- Verbose,
- Output,
- Warning,
- Error,
- }
-
internal enum Emoji
{
Default = 0,
@@ -66,61 +56,43 @@ public static string GetLogMessagePrefix(this Emoji emoji)
public static void Log(this ILogger logger, MessageDescriptor descriptor, params object?[] args)
{
logger.Log(
- descriptor.Severity.ToLogLevel(),
+ descriptor.Level,
descriptor.Id,
state: (descriptor, args),
exception: null,
formatter: static (state, _) => state.descriptor.GetMessage(state.args));
}
-
- public static LogLevel ToLogLevel(this MessageSeverity severity)
- => severity switch
- {
- MessageSeverity.None => LogLevel.None,
- MessageSeverity.Verbose => LogLevel.Debug,
- MessageSeverity.Output => LogLevel.Information,
- MessageSeverity.Warning => LogLevel.Warning,
- MessageSeverity.Error => LogLevel.Error,
- _ => throw new InvalidOperationException()
- };
-
- public static MessageSeverity ToSeverity(this LogLevel level)
- => level switch
- {
- LogLevel.Debug => MessageSeverity.Verbose,
- LogLevel.Information => MessageSeverity.Output,
- LogLevel.Warning => MessageSeverity.Warning,
- LogLevel.Error => MessageSeverity.Error,
- LogLevel.None => MessageSeverity.None,
- _ => throw new InvalidOperationException()
- };
}
- internal sealed class LoggerFactory(IReporter reporter) : ILoggerFactory
+ internal sealed class LoggerFactory(IReporter reporter, LogLevel level) : ILoggerFactory
{
- private sealed class Logger(IReporter reporter, string categoryName) : ILogger
+ private sealed class Logger(IReporter reporter, LogLevel level, string categoryName) : ILogger
{
public bool IsEnabled(LogLevel logLevel)
- => reporter.IsVerbose || logLevel > LogLevel.Debug;
+ => logLevel >= level;
public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter)
{
+ if (!IsEnabled(logLevel))
+ {
+ return;
+ }
+
var (name, display) = LoggingUtilities.ParseCategoryName(categoryName);
var prefix = display != null ? $"[{display}] " : "";
- var severity = logLevel.ToSeverity();
var descriptor = eventId.Id != 0 ? MessageDescriptor.GetDescriptor(eventId) : default;
- var emoji = severity switch
+ var emoji = logLevel switch
{
_ when descriptor.Emoji != Emoji.Default => descriptor.Emoji,
- MessageSeverity.Error => Emoji.Error,
- MessageSeverity.Warning => Emoji.Warning,
+ LogLevel.Error => Emoji.Error,
+ LogLevel.Warning => Emoji.Warning,
_ when MessageDescriptor.ComponentEmojis.TryGetValue(name, out var componentEmoji) => componentEmoji,
_ => Emoji.Watch
};
- reporter.Report(eventId, emoji, severity, prefix + formatter(state, exception));
+ reporter.Report(eventId, emoji, logLevel, prefix + formatter(state, exception));
}
public IDisposable? BeginScope(TState state) where TState : notnull
@@ -132,27 +104,27 @@ public void Dispose()
}
public ILogger CreateLogger(string categoryName)
- => new Logger(reporter, categoryName);
+ => new Logger(reporter, level, categoryName);
public void AddProvider(ILoggerProvider provider)
=> throw new NotImplementedException();
}
- internal readonly record struct MessageDescriptor(string Format, Emoji Emoji, MessageSeverity Severity, EventId Id)
+ internal readonly record struct MessageDescriptor(string Format, Emoji Emoji, LogLevel Level, EventId Id)
{
private static int s_id;
private static ImmutableDictionary s_descriptors = [];
- private static MessageDescriptor Create(string format, Emoji emoji, MessageSeverity severity)
+ private static MessageDescriptor Create(string format, Emoji emoji, LogLevel level)
// reserve event id 0 for ad-hoc messages
- => Create(new EventId(++s_id), format, emoji, severity);
+ => Create(new EventId(++s_id), format, emoji, level);
private static MessageDescriptor Create(LogEvent logEvent, Emoji emoji)
- => Create(logEvent.Id, logEvent.Message, emoji, logEvent.Level.ToSeverity());
+ => Create(logEvent.Id, logEvent.Message, emoji, logEvent.Level);
- private static MessageDescriptor Create(EventId id, string format, Emoji emoji, MessageSeverity severity)
+ private static MessageDescriptor Create(EventId id, string format, Emoji emoji, LogLevel level)
{
- var descriptor = new MessageDescriptor(format, emoji, severity, id.Id);
+ var descriptor = new MessageDescriptor(format, emoji, level, id.Id);
s_descriptors = s_descriptors.Add(id, descriptor);
return descriptor;
}
@@ -163,9 +135,18 @@ public static MessageDescriptor GetDescriptor(EventId id)
public string GetMessage(params object?[] args)
=> Id.Id == 0 ? Format : string.Format(Format, args);
- public MessageDescriptor WithSeverityWhen(MessageSeverity severity, bool condition)
- => condition && Severity != severity
- ? this with { Severity = severity, Emoji = severity switch { MessageSeverity.Error => Emoji.Error, MessageSeverity.Warning => Emoji.Warning, _ => Emoji } }
+ public MessageDescriptor WithLevelWhen(LogLevel level, bool condition)
+ => condition && Level != level
+ ? this with
+ {
+ Level = level,
+ Emoji = level switch
+ {
+ LogLevel.Error or LogLevel.Critical => Emoji.Error,
+ LogLevel.Warning => Emoji.Warning,
+ _ => Emoji
+ }
+ }
: this;
public static readonly ImmutableDictionary ComponentEmojis = ImmutableDictionary.Empty
@@ -179,83 +160,82 @@ public MessageDescriptor WithSeverityWhen(MessageSeverity severity, bool conditi
.Add(AspireServiceFactory.AspireLogComponentName, Emoji.Aspire);
// predefined messages used for testing:
- public static readonly MessageDescriptor HotReloadSessionStarting = Create("Hot reload session starting.", Emoji.HotReload, MessageSeverity.None);
- public static readonly MessageDescriptor HotReloadSessionStarted = Create("Hot reload session started.", Emoji.HotReload, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ProjectsRebuilt = Create("Projects rebuilt ({0})", Emoji.HotReload, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ProjectsRestarted = Create("Projects restarted ({0})", Emoji.HotReload, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ProjectDependenciesDeployed = Create("Project dependencies deployed ({0})", Emoji.HotReload, MessageSeverity.Verbose);
- public static readonly MessageDescriptor FixBuildError = Create("Fix the error to continue or press Ctrl+C to exit.", Emoji.Watch, MessageSeverity.Warning);
- public static readonly MessageDescriptor WaitingForChanges = Create("Waiting for changes", Emoji.Watch, MessageSeverity.Output);
- public static readonly MessageDescriptor LaunchedProcess = Create("Launched '{0}' with arguments '{1}': process id {2}", Emoji.Launch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor HotReloadChangeHandled = Create("Hot reload change handled in {0}ms.", Emoji.HotReload, MessageSeverity.Verbose);
+ public static readonly MessageDescriptor HotReloadSessionStarting = Create("Hot reload session starting.", Emoji.HotReload, LogLevel.None);
+ public static readonly MessageDescriptor HotReloadSessionStarted = Create("Hot reload session started.", Emoji.HotReload, LogLevel.Debug);
+ public static readonly MessageDescriptor ProjectsRebuilt = Create("Projects rebuilt ({0})", Emoji.HotReload, LogLevel.Debug);
+ public static readonly MessageDescriptor ProjectsRestarted = Create("Projects restarted ({0})", Emoji.HotReload, LogLevel.Debug);
+ public static readonly MessageDescriptor ProjectDependenciesDeployed = Create("Project dependencies deployed ({0})", Emoji.HotReload, LogLevel.Debug);
+ public static readonly MessageDescriptor FixBuildError = Create("Fix the error to continue or press Ctrl+C to exit.", Emoji.Watch, LogLevel.Warning);
+ public static readonly MessageDescriptor WaitingForChanges = Create("Waiting for changes", Emoji.Watch, LogLevel.Information);
+ public static readonly MessageDescriptor LaunchedProcess = Create("Launched '{0}' with arguments '{1}': process id {2}", Emoji.Launch, LogLevel.Debug);
+ public static readonly MessageDescriptor HotReloadChangeHandled = Create("Hot reload change handled in {0}ms.", Emoji.HotReload, LogLevel.Debug);
public static readonly MessageDescriptor HotReloadSucceeded = Create(LogEvents.HotReloadSucceeded, Emoji.HotReload);
public static readonly MessageDescriptor UpdatesApplied = Create(LogEvents.UpdatesApplied, Emoji.HotReload);
public static readonly MessageDescriptor Capabilities = Create(LogEvents.Capabilities, Emoji.HotReload);
- public static readonly MessageDescriptor WaitingForFileChangeBeforeRestarting = Create("Waiting for a file to change before restarting ...", Emoji.Wait, MessageSeverity.Warning);
- public static readonly MessageDescriptor WatchingWithHotReload = Create("Watching with Hot Reload.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor RestartInProgress = Create("Restart in progress.", Emoji.Restart, MessageSeverity.Output);
- public static readonly MessageDescriptor RestartRequested = Create("Restart requested.", Emoji.Restart, MessageSeverity.Output);
- public static readonly MessageDescriptor ShutdownRequested = Create("Shutdown requested. Press Ctrl+C again to force exit.", Emoji.Stop, MessageSeverity.Output);
- public static readonly MessageDescriptor ApplyUpdate_Error = Create("{0}{1}", Emoji.Error, MessageSeverity.Error);
- public static readonly MessageDescriptor ApplyUpdate_Warning = Create("{0}{1}", Emoji.Warning, MessageSeverity.Warning);
- public static readonly MessageDescriptor ApplyUpdate_Verbose = Create("{0}{1}", Emoji.Default, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ApplyUpdate_ChangingEntryPoint = Create("{0} Press \"Ctrl + R\" to restart.", Emoji.Warning, MessageSeverity.Warning);
- public static readonly MessageDescriptor ApplyUpdate_FileContentDoesNotMatchBuiltSource = Create("{0} Expected if a source file is updated that is linked to project whose build is not up-to-date.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ConfiguredToLaunchBrowser = Create("dotnet-watch is configured to launch a browser on ASP.NET Core application startup.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ConfiguredToUseBrowserRefresh = Create("Using browser-refresh middleware", Emoji.Default, MessageSeverity.Verbose);
- public static readonly MessageDescriptor SkippingConfiguringBrowserRefresh_SuppressedViaEnvironmentVariable = Create("Skipping configuring browser-refresh middleware since its refresh server suppressed via environment variable {0}.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported = Create("Skipping configuring browser-refresh middleware since the target framework version is not supported. For more information see 'https://aka.ms/dotnet/watch/unsupported-tfm'.", Emoji.Watch, MessageSeverity.Warning);
+ public static readonly MessageDescriptor WaitingForFileChangeBeforeRestarting = Create("Waiting for a file to change before restarting ...", Emoji.Wait, LogLevel.Warning);
+ public static readonly MessageDescriptor WatchingWithHotReload = Create("Watching with Hot Reload.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor RestartInProgress = Create("Restart in progress.", Emoji.Restart, LogLevel.Information);
+ public static readonly MessageDescriptor RestartRequested = Create("Restart requested.", Emoji.Restart, LogLevel.Information);
+ public static readonly MessageDescriptor ShutdownRequested = Create("Shutdown requested. Press Ctrl+C again to force exit.", Emoji.Stop, LogLevel.Information);
+ public static readonly MessageDescriptor ApplyUpdate_Error = Create("{0}{1}", Emoji.Error, LogLevel.Error);
+ public static readonly MessageDescriptor ApplyUpdate_Warning = Create("{0}{1}", Emoji.Warning, LogLevel.Warning);
+ public static readonly MessageDescriptor ApplyUpdate_Verbose = Create("{0}{1}", Emoji.Default, LogLevel.Debug);
+ public static readonly MessageDescriptor ApplyUpdate_ChangingEntryPoint = Create("{0} Press \"Ctrl + R\" to restart.", Emoji.Warning, LogLevel.Warning);
+ public static readonly MessageDescriptor ApplyUpdate_FileContentDoesNotMatchBuiltSource = Create("{0} Expected if a source file is updated that is linked to project whose build is not up-to-date.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor ConfiguredToLaunchBrowser = Create("dotnet-watch is configured to launch a browser on ASP.NET Core application startup.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor ConfiguredToUseBrowserRefresh = Create("Using browser-refresh middleware", Emoji.Default, LogLevel.Debug);
+ public static readonly MessageDescriptor SkippingConfiguringBrowserRefresh_SuppressedViaEnvironmentVariable = Create("Skipping configuring browser-refresh middleware since its refresh server suppressed via environment variable {0}.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported = Create("Skipping configuring browser-refresh middleware since the target framework version is not supported. For more information see 'https://aka.ms/dotnet/watch/unsupported-tfm'.", Emoji.Watch, LogLevel.Warning);
public static readonly MessageDescriptor UpdatingDiagnostics = Create(LogEvents.UpdatingDiagnostics, Emoji.Default);
public static readonly MessageDescriptor FailedToReceiveResponseFromConnectedBrowser = Create(LogEvents.FailedToReceiveResponseFromConnectedBrowser, Emoji.Default);
public static readonly MessageDescriptor NoBrowserConnected = Create(LogEvents.NoBrowserConnected, Emoji.Default);
- public static readonly MessageDescriptor LaunchingBrowser = Create("Launching browser: {0} {1}", Emoji.Default, MessageSeverity.Verbose);
+ public static readonly MessageDescriptor LaunchingBrowser = Create("Launching browser: {0} {1}", Emoji.Default, LogLevel.Debug);
public static readonly MessageDescriptor RefreshingBrowser = Create(LogEvents.RefreshingBrowser, Emoji.Default);
public static readonly MessageDescriptor ReloadingBrowser = Create(LogEvents.ReloadingBrowser, Emoji.Default);
public static readonly MessageDescriptor RefreshServerRunningAt = Create(LogEvents.RefreshServerRunningAt, Emoji.Default);
public static readonly MessageDescriptor ConnectedToRefreshServer = Create(LogEvents.ConnectedToRefreshServer, Emoji.Default);
- public static readonly MessageDescriptor RestartingApplicationToApplyChanges = Create("Restarting application to apply changes ...", Emoji.Default, MessageSeverity.Output);
- public static readonly MessageDescriptor RestartingApplication = Create("Restarting application ...", Emoji.Default, MessageSeverity.Output);
- public static readonly MessageDescriptor IgnoringChangeInHiddenDirectory = Create("Ignoring change in hidden directory '{0}': {1} '{2}'", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor IgnoringChangeInOutputDirectory = Create("Ignoring change in output directory: {0} '{1}'", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor IgnoringChangeInExcludedFile = Create("Ignoring change in excluded file '{0}': {1}. Path matches {2} glob '{3}' set in '{4}'.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor FileAdditionTriggeredReEvaluation = Create("File addition triggered re-evaluation.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ReEvaluationCompleted = Create("Re-evaluation completed.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ProjectChangeTriggeredReEvaluation = Create("Project change triggered re-evaluation.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor NoCSharpChangesToApply = Create("No C# changes to apply.", Emoji.Watch, MessageSeverity.Output);
- public static readonly MessageDescriptor Exited = Create("Exited", Emoji.Watch, MessageSeverity.Output);
- public static readonly MessageDescriptor ExitedWithUnknownErrorCode = Create("Exited with unknown error code", Emoji.Error, MessageSeverity.Error);
- public static readonly MessageDescriptor ExitedWithErrorCode = Create("Exited with error code {0}", Emoji.Error, MessageSeverity.Error);
- public static readonly MessageDescriptor FailedToLaunchProcess = Create("Failed to launch '{0}' with arguments '{1}': {2}", Emoji.Error, MessageSeverity.Error);
- public static readonly MessageDescriptor ApplicationFailed = Create("Application failed: {0}", Emoji.Error, MessageSeverity.Error);
- public static readonly MessageDescriptor ProcessRunAndExited = Create("Process id {0} ran for {1}ms and exited with exit code {2}.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor WaitingForProcessToExitWithin = Create("Waiting for process {0} to exit within {1}s.", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor WaitingForProcessToExit = Create("Waiting for process {0} to exit ({1}).", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor FailedToKillProcess = Create("Failed to kill process {0}: {1}.", Emoji.Error, MessageSeverity.Error);
- public static readonly MessageDescriptor TerminatingProcess = Create("Terminating process {0} ({1}).", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor FailedToSendSignalToProcess = Create("Failed to send {0} signal to process {1}: {2}", Emoji.Warning, MessageSeverity.Warning);
- public static readonly MessageDescriptor ErrorReadingProcessOutput = Create("Error reading {0} of process {1}: {2}", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor HotReloadOfScopedCssSucceeded = Create("Hot reload of scoped css succeeded.", Emoji.HotReload, MessageSeverity.Output);
- public static readonly MessageDescriptor HotReloadOfScopedCssPartiallySucceeded = Create("Hot reload of scoped css partially succeeded: {0} project(s) out of {1} were updated.", Emoji.HotReload, MessageSeverity.Output);
- public static readonly MessageDescriptor HotReloadOfScopedCssFailed = Create("Hot reload of scoped css failed.", Emoji.Error, MessageSeverity.Error);
- public static readonly MessageDescriptor HotReloadOfStaticAssetsSucceeded = Create("Hot reload of static assets succeeded.", Emoji.HotReload, MessageSeverity.Output);
+ public static readonly MessageDescriptor RestartingApplicationToApplyChanges = Create("Restarting application to apply changes ...", Emoji.Default, LogLevel.Information);
+ public static readonly MessageDescriptor RestartingApplication = Create("Restarting application ...", Emoji.Default, LogLevel.Information);
+ public static readonly MessageDescriptor IgnoringChangeInHiddenDirectory = Create("Ignoring change in hidden directory '{0}': {1} '{2}'", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor IgnoringChangeInOutputDirectory = Create("Ignoring change in output directory: {0} '{1}'", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor IgnoringChangeInExcludedFile = Create("Ignoring change in excluded file '{0}': {1}. Path matches {2} glob '{3}' set in '{4}'.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor FileAdditionTriggeredReEvaluation = Create("File addition triggered re-evaluation.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor ReEvaluationCompleted = Create("Re-evaluation completed.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor ProjectChangeTriggeredReEvaluation = Create("Project change triggered re-evaluation.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor NoCSharpChangesToApply = Create("No C# changes to apply.", Emoji.Watch, LogLevel.Information);
+ public static readonly MessageDescriptor Exited = Create("Exited", Emoji.Watch, LogLevel.Information);
+ public static readonly MessageDescriptor ExitedWithUnknownErrorCode = Create("Exited with unknown error code", Emoji.Error, LogLevel.Error);
+ public static readonly MessageDescriptor ExitedWithErrorCode = Create("Exited with error code {0}", Emoji.Error, LogLevel.Error);
+ public static readonly MessageDescriptor FailedToLaunchProcess = Create("Failed to launch '{0}' with arguments '{1}': {2}", Emoji.Error, LogLevel.Error);
+ public static readonly MessageDescriptor ApplicationFailed = Create("Application failed: {0}", Emoji.Error, LogLevel.Error);
+ public static readonly MessageDescriptor ProcessRunAndExited = Create("Process id {0} ran for {1}ms and exited with exit code {2}.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor WaitingForProcessToExitWithin = Create("Waiting for process {0} to exit within {1}s.", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor WaitingForProcessToExit = Create("Waiting for process {0} to exit ({1}).", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor FailedToKillProcess = Create("Failed to kill process {0}: {1}.", Emoji.Error, LogLevel.Error);
+ public static readonly MessageDescriptor TerminatingProcess = Create("Terminating process {0} ({1}).", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor FailedToSendSignalToProcess = Create("Failed to send {0} signal to process {1}: {2}", Emoji.Warning, LogLevel.Warning);
+ public static readonly MessageDescriptor ErrorReadingProcessOutput = Create("Error reading {0} of process {1}: {2}", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor HotReloadOfScopedCssSucceeded = Create("Hot reload of scoped css succeeded.", Emoji.HotReload, LogLevel.Information);
+ public static readonly MessageDescriptor HotReloadOfScopedCssPartiallySucceeded = Create("Hot reload of scoped css partially succeeded: {0} project(s) out of {1} were updated.", Emoji.HotReload, LogLevel.Information);
+ public static readonly MessageDescriptor HotReloadOfScopedCssFailed = Create("Hot reload of scoped css failed.", Emoji.Error, LogLevel.Error);
+ public static readonly MessageDescriptor HotReloadOfStaticAssetsSucceeded = Create("Hot reload of static assets succeeded.", Emoji.HotReload, LogLevel.Information);
public static readonly MessageDescriptor SendingStaticAssetUpdateRequest = Create(LogEvents.SendingStaticAssetUpdateRequest, Emoji.Default);
- public static readonly MessageDescriptor HotReloadCapabilities = Create("Hot reload capabilities: {0}.", Emoji.HotReload, MessageSeverity.Verbose);
- public static readonly MessageDescriptor HotReloadSuspended = Create("Hot reload suspended. To continue hot reload, press \"Ctrl + R\".", Emoji.HotReload, MessageSeverity.Output);
- public static readonly MessageDescriptor UnableToApplyChanges = Create("Unable to apply changes due to compilation errors.", Emoji.HotReload, MessageSeverity.Output);
- public static readonly MessageDescriptor RestartNeededToApplyChanges = Create("Restart is needed to apply the changes.", Emoji.HotReload, MessageSeverity.Output);
- public static readonly MessageDescriptor HotReloadEnabled = Create("Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload.", Emoji.HotReload, MessageSeverity.Output);
- public static readonly MessageDescriptor PressCtrlRToRestart = Create("Press Ctrl+R to restart.", Emoji.LightBulb, MessageSeverity.Output);
- public static readonly MessageDescriptor HotReloadCanceledProcessExited = Create("Hot reload canceled because the process exited.", Emoji.HotReload, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ApplicationKind_BlazorHosted = Create("Application kind: BlazorHosted. '{0}' references BlazorWebAssembly project '{1}'.", Emoji.Default, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ApplicationKind_BlazorWebAssembly = Create("Application kind: BlazorWebAssembly.", Emoji.Default, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ApplicationKind_WebApplication = Create("Application kind: WebApplication.", Emoji.Default, MessageSeverity.Verbose);
- public static readonly MessageDescriptor ApplicationKind_Default = Create("Application kind: Default.", Emoji.Default, MessageSeverity.Verbose);
- public static readonly MessageDescriptor WatchingFilesForChanges = Create("Watching {0} file(s) for changes", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor WatchingFilesForChanges_FilePath = Create("> {0}", Emoji.Watch, MessageSeverity.Verbose);
- public static readonly MessageDescriptor Building = Create("Building {0} ...", Emoji.Default, MessageSeverity.Output);
- public static readonly MessageDescriptor BuildSucceeded = Create("Build succeeded: {0}", Emoji.Default, MessageSeverity.Output);
- public static readonly MessageDescriptor BuildFailed = Create("Build failed: {0}", Emoji.Default, MessageSeverity.Output);
-
+ public static readonly MessageDescriptor HotReloadCapabilities = Create("Hot reload capabilities: {0}.", Emoji.HotReload, LogLevel.Debug);
+ public static readonly MessageDescriptor HotReloadSuspended = Create("Hot reload suspended. To continue hot reload, press \"Ctrl + R\".", Emoji.HotReload, LogLevel.Information);
+ public static readonly MessageDescriptor UnableToApplyChanges = Create("Unable to apply changes due to compilation errors.", Emoji.HotReload, LogLevel.Information);
+ public static readonly MessageDescriptor RestartNeededToApplyChanges = Create("Restart is needed to apply the changes.", Emoji.HotReload, LogLevel.Information);
+ public static readonly MessageDescriptor HotReloadEnabled = Create("Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload.", Emoji.HotReload, LogLevel.Information);
+ public static readonly MessageDescriptor PressCtrlRToRestart = Create("Press Ctrl+R to restart.", Emoji.LightBulb, LogLevel.Information);
+ public static readonly MessageDescriptor HotReloadCanceledProcessExited = Create("Hot reload canceled because the process exited.", Emoji.HotReload, LogLevel.Debug);
+ public static readonly MessageDescriptor ApplicationKind_BlazorHosted = Create("Application kind: BlazorHosted. '{0}' references BlazorWebAssembly project '{1}'.", Emoji.Default, LogLevel.Debug);
+ public static readonly MessageDescriptor ApplicationKind_BlazorWebAssembly = Create("Application kind: BlazorWebAssembly.", Emoji.Default, LogLevel.Debug);
+ public static readonly MessageDescriptor ApplicationKind_WebApplication = Create("Application kind: WebApplication.", Emoji.Default, LogLevel.Debug);
+ public static readonly MessageDescriptor ApplicationKind_Default = Create("Application kind: Default.", Emoji.Default, LogLevel.Debug);
+ public static readonly MessageDescriptor WatchingFilesForChanges = Create("Watching {0} file(s) for changes", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor WatchingFilesForChanges_FilePath = Create("> {0}", Emoji.Watch, LogLevel.Debug);
+ public static readonly MessageDescriptor Building = Create("Building {0} ...", Emoji.Default, LogLevel.Information);
+ public static readonly MessageDescriptor BuildSucceeded = Create("Build succeeded: {0}", Emoji.Default, LogLevel.Information);
+ public static readonly MessageDescriptor BuildFailed = Create("Build failed: {0}", Emoji.Default, LogLevel.Information);
}
internal interface IProcessOutputReporter
@@ -277,9 +257,6 @@ internal interface IProcessOutputReporter
internal interface IReporter
{
- void Report(EventId id, Emoji emoji, MessageSeverity severity, string message);
-
- public bool IsVerbose
- => false;
+ void Report(EventId id, Emoji emoji, LogLevel level, string message);
}
}
diff --git a/src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs b/src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs
index ba8e3317c62b..5a5306fe2ddf 100644
--- a/src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs
+++ b/src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs
@@ -5,12 +5,11 @@
using System.CommandLine;
using System.CommandLine.Parsing;
using System.Data;
-using System.Diagnostics;
-using Microsoft.Build.Logging;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.CommandLine;
+using Microsoft.DotNet.Cli.Commands.Build;
using Microsoft.DotNet.Cli.Commands.Run;
-using Microsoft.DotNet.Cli.Extensions;
+using Microsoft.DotNet.Cli.Commands.Test;
using Microsoft.Extensions.Logging;
namespace Microsoft.DotNet.Watch;
@@ -43,65 +42,15 @@ internal sealed class CommandLineOptions
public string Command => ExplicitCommand ?? DefaultCommand;
- // this option is referenced from inner logic and so needs to be reference-able
- public static Option NonInteractiveOption = new Option("--non-interactive") { Description = Resources.Help_NonInteractive, Arity = ArgumentArity.Zero };
-
public static CommandLineOptions? Parse(IReadOnlyList args, ILogger logger, TextWriter output, out int errorCode)
{
- // dotnet watch specific options:
- var quietOption = new Option("--quiet", "-q") { Description = Resources.Help_Quiet, Arity = ArgumentArity.Zero };
- var verboseOption = new Option("--verbose") { Description = Resources.Help_Verbose, Arity = ArgumentArity.Zero };
- var listOption = new Option("--list") { Description = Resources.Help_List, Arity = ArgumentArity.Zero };
- var noHotReloadOption = new Option("--no-hot-reload") { Description = Resources.Help_NoHotReload, Arity = ArgumentArity.Zero };
-
- verboseOption.Validators.Add(v =>
- {
- if (v.GetValue(quietOption) && v.GetValue(verboseOption))
- {
- v.AddError(Resources.Error_QuietAndVerboseSpecified);
- }
- });
-
- Option[] watchOptions =
- [
- quietOption,
- verboseOption,
- listOption,
- noHotReloadOption,
- NonInteractiveOption
- ];
-
- // Options we need to know about that are passed through to the subcommand:
- var shortProjectOption = new Option("-p") { Hidden = true, Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false };
- var longProjectOption = new Option("--project") { Hidden = true, Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false };
- var launchProfileOption = new Option("--launch-profile", "-lp") { Hidden = true, Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false };
- var noLaunchProfileOption = new Option("--no-launch-profile") { Hidden = true, Arity = ArgumentArity.Zero };
-
- var rootCommand = new RootCommand(Resources.Help)
- {
- Directives = { new EnvironmentVariablesDirective() },
- };
-
- foreach (var watchOption in watchOptions)
- {
- rootCommand.Options.Add(watchOption);
- }
-
- rootCommand.Options.Add(longProjectOption);
- rootCommand.Options.Add(shortProjectOption);
- rootCommand.Options.Add(launchProfileOption);
- rootCommand.Options.Add(noLaunchProfileOption);
-
- // We process all tokens that do not match any of the above options
- // to find the subcommand (the first unmatched token preceding "--")
- // and all its options and arguments.
- rootCommand.TreatUnmatchedTokensAsErrors = false;
+ var definition = new DotnetWatchCommandDefinition();
// We parse the command line outside of the action since the action
// might not be invoked in presence of unmatched tokens.
// We just need to know if the root command was invoked to handle --help.
var rootCommandInvoked = false;
- rootCommand.SetAction(parseResult => rootCommandInvoked = true);
+ definition.SetAction(parseResult => rootCommandInvoked = true);
ParserConfiguration parseConfig = new()
{
@@ -110,7 +59,7 @@ internal sealed class CommandLineOptions
};
// parse without forwarded options first:
- var parseResult = rootCommand.Parse(args, parseConfig);
+ var parseResult = definition.Parse(args, parseConfig);
if (ReportErrors(parseResult, logger))
{
errorCode = 1;
@@ -124,11 +73,11 @@ internal sealed class CommandLineOptions
foreach (var buildOption in buildOptions)
{
- rootCommand.Options.Add(buildOption);
+ definition.Options.Add(buildOption);
}
// reparse with forwarded options:
- parseResult = rootCommand.Parse(args, parseConfig);
+ parseResult = definition.Parse(args, parseConfig);
if (ReportErrors(parseResult, logger))
{
errorCode = 1;
@@ -148,10 +97,10 @@ internal sealed class CommandLineOptions
return null;
}
- var projectValue = parseResult.GetValue(longProjectOption);
+ var projectValue = parseResult.GetValue(definition.LongProjectOption);
if (string.IsNullOrEmpty(projectValue))
{
- var projectShortValue = parseResult.GetValue(shortProjectOption);
+ var projectShortValue = parseResult.GetValue(definition.ShortProjectOption);
if (!string.IsNullOrEmpty(projectShortValue))
{
logger.LogWarning(Resources.Warning_ProjectAbbreviationDeprecated);
@@ -159,7 +108,12 @@ internal sealed class CommandLineOptions
}
}
- var commandArguments = GetCommandArguments(parseResult, watchOptions, explicitCommand, out var binLogToken, out var binLogPath);
+ var commandArguments = GetCommandArguments(
+ parseResult,
+ command,
+ explicitCommand,
+ out var binLogToken,
+ out var binLogPath);
// We assume that forwarded options, if any, are intended for dotnet build.
var buildArguments = buildOptions.Select(option => option.ForwardingFunction!(parseResult)).SelectMany(args => args).ToList();
@@ -171,15 +125,20 @@ internal sealed class CommandLineOptions
var targetFrameworkOption = (Option?)buildOptions.SingleOrDefault(option => option.Name == "--framework");
+ var logLevel = parseResult.GetValue(definition.VerboseOption)
+ ? LogLevel.Debug
+ : parseResult.GetValue(definition.QuietOption)
+ ? LogLevel.Warning
+ : LogLevel.Information;
+
return new()
{
- List = parseResult.GetValue(listOption),
+ List = parseResult.GetValue(definition.ListOption),
GlobalOptions = new()
{
- Quiet = parseResult.GetValue(quietOption),
- NoHotReload = parseResult.GetValue(noHotReloadOption),
- NonInteractive = parseResult.GetValue(NonInteractiveOption),
- Verbose = parseResult.GetValue(verboseOption),
+ LogLevel = logLevel,
+ NoHotReload = parseResult.GetValue(definition.NoHotReloadOption),
+ NonInteractive = parseResult.GetValue(definition.NonInteractiveOption),
BinaryLogPath = ParseBinaryLogFilePath(binLogPath),
},
@@ -187,8 +146,8 @@ internal sealed class CommandLineOptions
ExplicitCommand = explicitCommand?.Name,
ProjectPath = projectValue,
- LaunchProfileName = parseResult.GetValue(launchProfileOption),
- NoLaunchProfile = parseResult.GetValue(noLaunchProfileOption),
+ LaunchProfileName = parseResult.GetValue(definition.LaunchProfileOption),
+ NoLaunchProfile = parseResult.GetValue(definition.NoLaunchProfileOption),
BuildArguments = buildArguments,
TargetFramework = targetFrameworkOption != null ? parseResult.GetValue(targetFrameworkOption) : null,
};
@@ -216,11 +175,13 @@ internal sealed class CommandLineOptions
private static IReadOnlyList GetCommandArguments(
ParseResult parseResult,
- IReadOnlyList