Skip to content

Commit 2011c88

Browse files
committed
Reduce CLI diagnostic log noise and improve process logging
1 parent 782790b commit 2011c88

16 files changed

+67
-64
lines changed

src/Aspire.Cli/Configuration/Features.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,29 @@ internal sealed class Features(IConfiguration configuration, ILogger<Features> l
1111
public bool IsFeatureEnabled(string feature, bool defaultValue)
1212
{
1313
var configKey = $"features:{feature}";
14-
14+
1515
var value = configuration[configKey];
16-
16+
1717
logger.LogTrace("Feature check: {Feature}, ConfigKey: {ConfigKey}, Value: '{Value}', DefaultValue: {DefaultValue}",
1818
feature, configKey, value ?? "(null)", defaultValue);
19-
19+
2020
if (string.IsNullOrEmpty(value))
2121
{
22-
logger.LogDebug("Feature {Feature} using default value: {DefaultValue}", feature, defaultValue);
22+
logger.LogTrace("Feature {Feature} using default value: {DefaultValue}", feature, defaultValue);
2323
return defaultValue;
2424
}
25-
25+
2626
var enabled = bool.TryParse(value, out var parsed) && parsed;
2727
logger.LogDebug("Feature {Feature} parsed value: {Enabled}", feature, enabled);
2828
return enabled;
2929
}
30+
31+
public void LogFeatureState()
32+
{
33+
foreach (var metadata in KnownFeatures.GetAllFeatureMetadata())
34+
{
35+
var value = IsFeatureEnabled(metadata.Name, metadata.DefaultValue);
36+
logger.LogDebug("Feature {Feature} = {Value} (default: {DefaultValue})", metadata.Name, value, metadata.DefaultValue);
37+
}
38+
}
3039
}

src/Aspire.Cli/Configuration/IFeatures.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ namespace Aspire.Cli.Configuration;
66
internal interface IFeatures
77
{
88
bool IsFeatureEnabled(string featureFlag, bool defaultValue);
9+
void LogFeatureState();
910
}

src/Aspire.Cli/DotNet/ProcessExecution.cs

Lines changed: 19 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -43,41 +43,31 @@ internal ProcessExecution(Process process, ILogger logger, ProcessInvocationOpti
4343
/// <inheritdoc />
4444
public bool Start()
4545
{
46-
var suppressLogging = _options.SuppressLogging;
47-
4846
var started = _process.Start();
4947

5048
if (!started)
5149
{
52-
if (!suppressLogging)
53-
{
54-
_logger.LogDebug("Failed to start process {FileName} with args: {Args}", FileName, string.Join(" ", Arguments));
55-
}
50+
_logger.LogDebug("Failed to start process {FileName} with args: {Args}", FileName, string.Join(" ", Arguments));
5651
return false;
5752
}
5853

59-
if (!suppressLogging)
60-
{
61-
_logger.LogDebug("Started {FileName} with PID: {ProcessId}", FileName, _process.Id);
62-
}
54+
_logger.LogDebug("Started {FileName} in {WorkingDirectory} with PID: {ProcessId}", FileName, _process.StartInfo.WorkingDirectory, _process.Id);
6355

6456
// Start stream forwarders
6557
_stdoutForwarder = Task.Run(async () =>
6658
{
6759
await ForwardStreamToLoggerAsync(
6860
_process.StandardOutput,
6961
"stdout",
70-
_options.StandardOutputCallback,
71-
suppressLogging);
62+
_options.StandardOutputCallback);
7263
});
7364

7465
_stderrForwarder = Task.Run(async () =>
7566
{
7667
await ForwardStreamToLoggerAsync(
7768
_process.StandardError,
7869
"stderr",
79-
_options.StandardErrorCallback,
80-
suppressLogging);
70+
_options.StandardErrorCallback);
8171
});
8272

8373
return true;
@@ -86,29 +76,18 @@ await ForwardStreamToLoggerAsync(
8676
/// <inheritdoc />
8777
public async Task<int> WaitForExitAsync(CancellationToken cancellationToken)
8878
{
89-
var suppressLogging = _options.SuppressLogging;
90-
91-
if (!suppressLogging)
92-
{
93-
_logger.LogDebug("Waiting for process to exit with PID: {ProcessId}", _process.Id);
94-
}
79+
_logger.LogDebug("Waiting for process to exit with PID: {ProcessId}", _process.Id);
9580

9681
await _process.WaitForExitAsync(cancellationToken);
9782

9883
if (!_process.HasExited)
9984
{
100-
if (!suppressLogging)
101-
{
102-
_logger.LogDebug("Process with PID: {ProcessId} has not exited, killing it.", _process.Id);
103-
}
85+
_logger.LogDebug("Process with PID: {ProcessId} has not exited, killing it.", _process.Id);
10486
_process.Kill(false);
10587
}
10688
else
10789
{
108-
if (!suppressLogging)
109-
{
110-
_logger.LogDebug("Process with PID: {ProcessId} has exited with code: {ExitCode}", _process.Id, _process.ExitCode);
111-
}
90+
_logger.LogDebug("Process with PID: {ProcessId} has exited with code: {ExitCode}", _process.Id, _process.ExitCode);
11291
}
11392

11493
// Explicitly close the streams to unblock any pending ReadLineAsync calls.
@@ -153,32 +132,25 @@ public void Dispose()
153132
_process.Dispose();
154133
}
155134

156-
private async Task ForwardStreamToLoggerAsync(StreamReader reader, string identifier, Action<string>? lineCallback, bool suppressLogging)
135+
private async Task ForwardStreamToLoggerAsync(StreamReader reader, string identifier, Action<string>? lineCallback)
157136
{
158-
if (!suppressLogging)
159-
{
160-
_logger.LogDebug(
161-
"Starting to forward stream with identifier '{Identifier}' on process '{ProcessId}' to logger",
162-
identifier,
163-
_process.Id
164-
);
165-
}
137+
_logger.LogDebug(
138+
"Starting to forward stream with identifier '{Identifier}' on process '{ProcessId}' to logger",
139+
identifier,
140+
_process.Id
141+
);
166142

167143
try
168144
{
169145
string? line;
170146
while ((line = await reader.ReadLineAsync()) is not null)
171147
{
172-
if (!suppressLogging)
173-
{
174-
_logger.LogTrace(
175-
"{FileName}({ProcessId}) {Identifier}: {Line}",
176-
FileName,
177-
_process.Id,
178-
identifier,
179-
line
180-
);
181-
}
148+
_logger.LogTrace(
149+
"({ProcessId}) {Identifier}: {Line}",
150+
_process.Id,
151+
identifier,
152+
line
153+
);
182154
lineCallback?.Invoke(line);
183155
}
184156
}

src/Aspire.Cli/DotNet/ProcessExecutionFactory.cs

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

44
using System.Diagnostics;
55
using Microsoft.Extensions.Logging;
6+
using Microsoft.Extensions.Logging.Abstractions;
67

78
namespace Aspire.Cli.DotNet;
89

@@ -14,18 +15,15 @@ internal sealed class ProcessExecutionFactory(
1415
{
1516
public IProcessExecution CreateExecution(string fileName, string[] args, IDictionary<string, string>? env, DirectoryInfo workingDirectory, ProcessInvocationOptions options)
1617
{
17-
var suppressLogging = options.SuppressLogging;
18+
var effectiveLogger = options.SuppressLogging ? (ILogger)NullLogger.Instance : logger;
1819

19-
if (!suppressLogging)
20-
{
21-
logger.LogDebug("Running {FullName} with args: {Args}", workingDirectory.FullName, string.Join(" ", args));
20+
effectiveLogger.LogDebug("Running {FileName} in {WorkingDirectory} with args: {Args}", fileName, workingDirectory.FullName, string.Join(" ", args));
2221

23-
if (env is not null)
22+
if (env is not null)
23+
{
24+
foreach (var envKvp in env)
2425
{
25-
foreach (var envKvp in env)
26-
{
27-
logger.LogDebug("Running {FullName} with env: {EnvKey}={EnvValue}", workingDirectory.FullName, envKvp.Key, envKvp.Value);
28-
}
26+
effectiveLogger.LogDebug("{FileName} env: {EnvKey}={EnvValue}", fileName, envKvp.Key, envKvp.Value);
2927
}
3028
}
3129

@@ -53,6 +51,6 @@ public IProcessExecution CreateExecution(string fileName, string[] args, IDictio
5351
}
5452

5553
var process = new Process { StartInfo = startInfo };
56-
return new ProcessExecution(process, logger, options);
54+
return new ProcessExecution(process, effectiveLogger, options);
5755
}
5856
}

src/Aspire.Cli/NuGet/BundleNuGetPackageCache.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ private async Task<IEnumerable<NuGetPackage>> SearchPackagesInternalAsync(
211211

212212
private IEnumerable<NuGetPackage> FilterPackages(IEnumerable<NuGetPackage> packages, Func<string, bool>? filter)
213213
{
214+
var showDeprecatedPackages = _features.IsFeatureEnabled(KnownFeatures.ShowDeprecatedPackages, defaultValue: false);
214215
var effectiveFilter = (NuGetPackage p) =>
215216
{
216217
if (filter is not null)
@@ -221,7 +222,7 @@ private IEnumerable<NuGetPackage> FilterPackages(IEnumerable<NuGetPackage> packa
221222
var isOfficialPackage = IsOfficialOrCommunityToolkitPackage(p.Id);
222223

223224
// Apply deprecated package filter unless the user wants to show deprecated packages
224-
if (isOfficialPackage && !_features.IsFeatureEnabled(KnownFeatures.ShowDeprecatedPackages, defaultValue: false))
225+
if (isOfficialPackage && !showDeprecatedPackages)
225226
{
226227
return !DeprecatedPackages.IsDeprecated(p.Id);
227228
}

src/Aspire.Cli/NuGet/NuGetPackageCache.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ public async Task<IEnumerable<NuGetPackage>> GetPackagesAsync(DirectoryInfo work
130130

131131
// If no specific filter is specified we use the fallback filter which is useful in most circumstances
132132
// other that aspire update which really needs to see all the packages to work effectively.
133+
var showDeprecatedPackages = features.IsFeatureEnabled(KnownFeatures.ShowDeprecatedPackages, defaultValue: false);
133134
var effectiveFilter = (NuGetPackage p) =>
134135
{
135136
if (filter is not null)
@@ -140,7 +141,7 @@ public async Task<IEnumerable<NuGetPackage>> GetPackagesAsync(DirectoryInfo work
140141
var isOfficialPackage = IsOfficialOrCommunityToolkitPackage(p.Id);
141142

142143
// Apply deprecated package filter unless the user wants to show deprecated packages
143-
if (isOfficialPackage && !features.IsFeatureEnabled(KnownFeatures.ShowDeprecatedPackages, defaultValue: false))
144+
if (isOfficialPackage && !showDeprecatedPackages)
144145
{
145146
return !DeprecatedPackages.IsDeprecated(p.Id);
146147
}

src/Aspire.Cli/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,9 @@ public static async Task<int> Main(string[] args)
718718
var telemetry = app.Services.GetRequiredService<AspireCliTelemetry>();
719719
var telemetryManager = app.Services.GetRequiredService<TelemetryManager>();
720720

721+
// Log feature state at startup for diagnostics
722+
app.Services.GetRequiredService<IFeatures>().LogFeatureState();
723+
721724
// Display first run experience if this is the first time the CLI is run on this machine
722725
await DisplayFirstTimeUseNoticeIfNeededAsync(app.Services, args, cts.Token);
723726

tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,6 +1862,8 @@ public bool IsFeatureEnabled(string featureFlag, bool defaultValue)
18621862
_ => defaultValue
18631863
};
18641864
}
1865+
1866+
public void LogFeatureState() { }
18651867
}
18661868

18671869
internal sealed class TestTypeScriptStarterProjectFactory(Func<DirectoryInfo, CancellationToken, Task<bool>> buildAndGenerateSdkAsync) : IAppHostProjectFactory

tests/Aspire.Cli.Tests/Commands/RunCommandTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,8 @@ public bool IsFeatureEnabled(string featureName, bool defaultValue = false)
14951495
{
14961496
return _features.TryGetValue(featureName, out var value) ? value : defaultValue;
14971497
}
1498+
1499+
public void LogFeatureState() { }
14981500
}
14991501

15001502
[Fact]

tests/Aspire.Cli.Tests/Configuration/KnownFeaturesTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,7 @@ public bool IsFeatureEnabled(string featureFlag, bool defaultValue)
106106

107107
return defaultValue;
108108
}
109+
110+
public void LogFeatureState() { }
109111
}
110112
}

0 commit comments

Comments
 (0)