Skip to content

Commit ed549fd

Browse files
Change CLI flag --LogLevel to --log-level (#3653)
## Why make this change? - This solves issue #3257 ## What is this change? We changed the `dab start` `--LogLevel` flag to `--log-level` in order to have it follow the conventions we already have. We also allow for the CLI to still use the previous `--LogLevel` flag but add a warning sign to ensure the user knows it is deprecated. Lastly, also changed all of the comments that mention the previous `--LogLevel` and updated it to use the new `--log-level`. Added new `isLogLevelLegacy ` value that is passed into the `DynamicLogLevelProvider.cs` which would then be used to add the log in case the deprecated flag is used in an Aspire scenario. In which the flag is passed to the dll directly and not through CLI ## How was this tested? - [ ] Integration Tests - [X] Unit Tests Changed the tests that were for `--LogLevel` so they now use `--log-level` and added a few extra cases to ensure the `--LogLevel` flag still works ## Sample Request(s) dab start --log-level information dab start --LogLevel warning
1 parent d50e607 commit ed549fd

14 files changed

Lines changed: 101 additions & 63 deletions

File tree

src/Azure.DataApiBuilder.Mcp/Core/McpStdioServer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ private void HandleListTools(JsonElement? id)
316316
/// <remarks>
317317
/// Log level precedence (highest to lowest):
318318
/// 1. MCP <c>logging/setLevel</c> (Agent) - always wins, overrides CLI and Config.
319-
/// 2. CLI <c>--LogLevel</c> flag.
319+
/// 2. CLI <c>--log-level</c> flag.
320320
/// 3. Config <c>runtime.telemetry.log-level</c>.
321321
/// 4. Default: <c>None</c> for MCP stdio mode (silent by default to keep stdout clean for JSON-RPC),
322322
/// <c>Error</c> in Production, <c>Debug</c> in Development.
@@ -334,7 +334,7 @@ private void HandleListTools(JsonElement? id)
334334
/// hot-reloads do not overwrite the agent's choice.
335335
/// 3. Restore <see cref="Console.Error"/> to the real stderr stream when logging is enabled,
336336
/// in case startup redirected it to <see cref="TextWriter.Null"/> (default for
337-
/// <c>--mcp-stdio</c> or <c>--LogLevel none</c>).
337+
/// <c>--mcp-stdio</c> or <c>--log-level none</c>).
338338
/// </remarks>
339339
private void HandleSetLogLevel(JsonElement? id, JsonElement root)
340340
{
@@ -393,7 +393,7 @@ private void HandleSetLogLevel(JsonElement? id, JsonElement root)
393393
bool updated = logLevelController.UpdateFromMcp(level);
394394

395395
// Restore stderr if the agent successfully turned logging on. When `--mcp-stdio` (or
396-
// `--LogLevel none`) was the startup default, stderr was redirected to TextWriter.Null;
396+
// `--log-level none`) was the startup default, stderr was redirected to TextWriter.Null;
397397
// re-enable it now so subsequent logs flow.
398398
if (updated && isLoggingEnabled)
399399
{

src/Cli.Tests/CustomLoggerTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Cli.Tests;
66
/// <summary>
77
/// Tests for <see cref="CustomLoggerProvider"/> covering both the standard CLI
88
/// path (writes to stdout/stderr with abbreviated labels) and the MCP stdio path
9-
/// (suppressed by default, opt-in via either CLI <c>--LogLevel</c> or the
9+
/// (suppressed by default, opt-in via either CLI <c>--log-level</c> or the
1010
/// runtime config's <c>log-level</c>, always routed to stderr to keep the
1111
/// JSON-RPC channel on stdout uncorrupted).
1212
/// </summary>
@@ -85,7 +85,7 @@ public void LogOutput_UsesAbbreviatedLogLevelLabels(LogLevel logLevel, string ex
8585
}
8686

8787
/// <summary>
88-
/// MCP stdio mode with no overrides (neither CLI <c>--LogLevel</c> nor
88+
/// MCP stdio mode with no overrides (neither CLI <c>--log-level</c> nor
8989
/// config <c>log-level</c>): all output must be suppressed so the JSON-RPC
9090
/// channel stays clean.
9191
/// </summary>
@@ -106,7 +106,7 @@ public void Mcp_NoOverrides_SuppressesAllOutput()
106106
}
107107

108108
/// <summary>
109-
/// MCP stdio mode with a CLI-supplied <c>--LogLevel</c>: logs must always
109+
/// MCP stdio mode with a CLI-supplied <c>--log-level</c>: logs must always
110110
/// go to stderr (never stdout) and the level threshold from
111111
/// <see cref="Cli.Utils.CliLogLevel"/> must be honored.
112112
/// </summary>

src/Cli.Tests/EndToEndTests.cs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -814,23 +814,25 @@ public Task TestUpdatingStoredProcedureWithRestMethods()
814814
}
815815

816816
/// <summary>
817-
/// Test to validate that the engine starts successfully when --verbose and --LogLevel
817+
/// Test to validate that the engine starts successfully when --verbose and --log-level
818818
/// options are used with the start command
819819
/// This test does not validate whether the engine logs messages at the specified log level
820820
/// </summary>
821821
/// <param name="logLevelOption">Log level options</param>
822822
[DataTestMethod]
823823
[DataRow("", DisplayName = "No logging from command line.")]
824824
[DataRow("--verbose", DisplayName = "Verbose logging from command line.")]
825-
[DataRow("--LogLevel 0", DisplayName = "LogLevel 0 from command line.")]
826-
[DataRow("--LogLevel 1", DisplayName = "LogLevel 1 from command line.")]
827-
[DataRow("--LogLevel 2", DisplayName = "LogLevel 2 from command line.")]
828-
[DataRow("--LogLevel Trace", DisplayName = "LogLevel Trace from command line.")]
829-
[DataRow("--LogLevel Debug", DisplayName = "LogLevel Debug from command line.")]
830-
[DataRow("--LogLevel Information", DisplayName = "LogLevel Information from command line.")]
831-
[DataRow("--LogLevel tRace", DisplayName = "Case sensitivity: LogLevel Trace from command line.")]
832-
[DataRow("--LogLevel DebUG", DisplayName = "Case sensitivity: LogLevel Debug from command line.")]
833-
[DataRow("--LogLevel information", DisplayName = "Case sensitivity: LogLevel Information from command line.")]
825+
[DataRow("--log-level 0", DisplayName = "LogLevel 0 from command line.")]
826+
[DataRow("--log-level 1", DisplayName = "LogLevel 1 from command line.")]
827+
[DataRow("--log-level 2", DisplayName = "LogLevel 2 from command line.")]
828+
[DataRow("--log-level Trace", DisplayName = "LogLevel Trace from command line.")]
829+
[DataRow("--log-level Debug", DisplayName = "LogLevel Debug from command line.")]
830+
[DataRow("--log-level Information", DisplayName = "LogLevel Information from command line.")]
831+
[DataRow("--log-level tRace", DisplayName = "Case sensitivity: LogLevel Trace from command line.")]
832+
[DataRow("--log-level DebUG", DisplayName = "Case sensitivity: LogLevel Debug from command line.")]
833+
[DataRow("--log-level information", DisplayName = "Case sensitivity: LogLevel Information from command line.")]
834+
[DataRow("--LogLevel 0", DisplayName = "Case sensitivity: LogLevel 0 legacy from command line.")]
835+
[DataRow("--LogLevel information", DisplayName = "Case sensitivity: LogLevel Information legacy from command line.")]
834836
public void TestEngineStartUpWithVerboseAndLogLevelOptions(string logLevelOption)
835837
{
836838
_fileSystem!.File.WriteAllText(TEST_RUNTIME_CONFIG_FILE, INITIAL_CONFIG);
@@ -850,7 +852,7 @@ public void TestEngineStartUpWithVerboseAndLogLevelOptions(string logLevelOption
850852
}
851853

852854
/// <summary>
853-
/// Test to validate that the engine starts successfully when --LogLevel is set to Warning
855+
/// Test to validate that the engine starts successfully when --log-level is set to Warning
854856
/// or above. At these levels, CLI phase messages (logged at Information) are suppressed,
855857
/// so no stdout output with message 'info' is expected during the CLI phase.
856858
/// </summary>
@@ -871,7 +873,7 @@ public async Task TestEngineStartUpWithHighLogLevelOptions(string logLevelOption
871873
StringWriter consoleOutput = new();
872874
Console.SetOut(consoleOutput);
873875

874-
string[] args = { "start", "--config", TEST_RUNTIME_CONFIG_FILE, "--LogLevel", logLevelOption };
876+
string[] args = { "start", "--config", TEST_RUNTIME_CONFIG_FILE, "--log-level", logLevelOption };
875877
_fileSystem!.File.WriteAllText(TEST_RUNTIME_CONFIG_FILE, INITIAL_CONFIG);
876878

877879
// Run Program.Execute on a background task because StartEngine blocks until the host shuts down.
@@ -886,7 +888,7 @@ public async Task TestEngineStartUpWithHighLogLevelOptions(string logLevelOption
886888
}
887889

888890
/// <summary>
889-
/// Test to validate that the engine starts successfully when --LogLevel is set to None.
891+
/// Test to validate that the engine starts successfully when --log-level is set to None.
890892
/// At these levels, CLI phase messages (logged at Information) are suppressed,
891893
/// so no stdout output is expected during the CLI phase.
892894
/// </summary>
@@ -901,7 +903,7 @@ public async Task TestEngineStartUpWithLogLevelNone(string logLevelOption)
901903
StringWriter consoleOutput = new();
902904
Console.SetOut(consoleOutput);
903905

904-
string[] args = { "start", "--config", TEST_RUNTIME_CONFIG_FILE, "--LogLevel", logLevelOption };
906+
string[] args = { "start", "--config", TEST_RUNTIME_CONFIG_FILE, "--log-level", logLevelOption };
905907
_fileSystem!.File.WriteAllText(TEST_RUNTIME_CONFIG_FILE, INITIAL_CONFIG);
906908

907909
// Run Program.Execute on a background task because StartEngine blocks until the host shuts down.
@@ -915,12 +917,12 @@ public async Task TestEngineStartUpWithLogLevelNone(string logLevelOption)
915917
}
916918

917919
/// Validates that `dab start` correctly sets <see cref="Startup.IsCliOverriding"/>
918-
/// based on whether the --LogLevel CLI flag is provided.
920+
/// based on whether the --log-level CLI flag is provided.
919921
///
920-
/// When the --LogLevel flag is provided, IsCliOverriding should be true.
921-
/// When the --LogLevel flag is omitted (log level comes from the config file), IsCliOverriding should be false.
922+
/// When the --log-level flag is provided, IsCliOverriding should be true.
923+
/// When the --log-level flag is omitted (log level comes from the config file), IsCliOverriding should be false.
922924
/// </summary>
923-
/// <param name="cliLogLevel">The --LogLevel CLI flag value, or null to omit the flag.</param>
925+
/// <param name="cliLogLevel">The --log-level CLI flag value, or null to omit the flag.</param>
924926
/// <param name="expectedIsOverridden">Expected value of Startup.IsCliOverriding.</param>
925927
[DataTestMethod]
926928
[DataRow(null, false, DisplayName = "IsCliOverriding is false")]
@@ -978,6 +980,7 @@ public async Task TestStartCommandResolvesLogLevelFromConfigOrFlag(
978980
isHttpsRedirectionDisabled: false,
979981
mcpStdio: false,
980982
mcpRole: null,
983+
logLevelLegacy: null,
981984
config: TEST_RUNTIME_CONFIG_FILE);
982985

983986
// Run TryStartEngineWithOptions on a background task because StartEngine blocks until the host shuts down.

src/Cli/Commands/StartOptions.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,24 @@ public class StartOptions : Options
2121

2222
public LogBuffer CliBuffer { get; }
2323

24-
public StartOptions(bool verbose, LogLevel? logLevel, bool isHttpsRedirectionDisabled, bool mcpStdio, string? mcpRole, string config)
24+
public StartOptions(bool verbose, LogLevel? logLevel, bool isHttpsRedirectionDisabled, bool mcpStdio, string? mcpRole, LogLevel? logLevelLegacy, string config)
2525
: base(config)
2626
{
2727
// When verbose is true we set LogLevel to information.
2828
LogLevel = verbose is true ? Microsoft.Extensions.Logging.LogLevel.Information : logLevel;
29+
LogLevelLegacy = logLevelLegacy;
2930
IsHttpsRedirectionDisabled = isHttpsRedirectionDisabled;
3031
McpStdio = mcpStdio;
3132
McpRole = mcpRole;
3233
CliBuffer = new LogBuffer();
3334
}
3435

3536
// SetName defines mutually exclusive sets, ie: can not have
36-
// both verbose and LogLevel.
37+
// both verbose and log-level.
3738
[Option("verbose", SetName = "verbose", Required = false, HelpText = "Specifies logging level as informational.")]
3839
public bool Verbose { get; }
3940

40-
[Option("LogLevel", SetName = "LogLevel", Required = false, HelpText = LOGLEVEL_HELPTEXT)]
41+
[Option("log-level", SetName = "loglevel", Required = false, HelpText = LOGLEVEL_HELPTEXT)]
4142
public LogLevel? LogLevel { get; }
4243

4344
[Option("no-https-redirect", Required = false, HelpText = "Disables automatic https redirects.")]
@@ -49,6 +50,9 @@ public StartOptions(bool verbose, LogLevel? logLevel, bool isHttpsRedirectionDis
4950
[Value(0, MetaName = "role", Required = false, HelpText = "Optional MCP permissions role, e.g. role:anonymous. If omitted, defaults to anonymous.")]
5051
public string? McpRole { get; }
5152

53+
[Option("LogLevel", SetName = "LogLevel", Required = false, HelpText = LOGLEVEL_HELPTEXT, Hidden = true)]
54+
public LogLevel? LogLevelLegacy { get; }
55+
5256
public int Handler(ILogger logger, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem)
5357
{
5458
CliBuffer.BufferLog(Microsoft.Extensions.Logging.LogLevel.Information, $"{PRODUCT_NAME} {ProductInfo.GetProductVersion()}");

src/Cli/ConfigGenerator.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3066,10 +3066,10 @@ public static bool TryStartEngineWithOptions(StartOptions options, FileSystemRun
30663066
List<string> args = new()
30673067
{ "--ConfigFileName", runtimeConfigFile };
30683068

3069-
/// Add arguments for LogLevel. Only pass --LogLevel when user explicitly specified it,
3069+
/// Add arguments for LogLevel. Only pass --log-level when user explicitly specified it,
30703070
/// so that MCP logging/setLevel can still adjust the level when no CLI override is present.
30713071
///
3072-
/// When --LogLevel is NOT specified:
3072+
/// When --log-level is NOT specified:
30733073
/// - MCP stdio mode: Service defaults to None for clean stdout output
30743074
/// - Non-MCP mode: Service defaults to Debug (Development) or Error (Production) based on config
30753075
LogLevel minimumLogLevel;
@@ -3079,19 +3079,30 @@ public static bool TryStartEngineWithOptions(StartOptions options, FileSystemRun
30793079
Utils.IsConfigOverriding = false;
30803080
Utils.ConfigLogLevel = LogLevel.Information;
30813081

3082+
LogLevel? logLevel = null;
30823083
if (options.LogLevel is not null)
30833084
{
3084-
if (options.LogLevel is < LogLevel.Trace or > LogLevel.None)
3085+
logLevel = options.LogLevel;
3086+
}
3087+
else if (options.LogLevelLegacy is not null)
3088+
{
3089+
options.CliBuffer.BufferLog(LogLevel.Warning, $"--LogLevel is deprecated, please use --log-level instead.");
3090+
logLevel = options.LogLevelLegacy;
3091+
}
3092+
3093+
if (logLevel is not null)
3094+
{
3095+
if (logLevel is < LogLevel.Trace or > LogLevel.None)
30853096
{
30863097
options.CliBuffer.BufferLog(LogLevel.Error,
3087-
$"LogLevel's valid range is 0 to 6, your value: {options.LogLevel}, see: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.loglevel");
3098+
$"LogLevel's valid range is 0 to 6, your value: {logLevel}, see: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.loglevel");
30883099
return false;
30893100
}
30903101

3091-
minimumLogLevel = (LogLevel)options.LogLevel;
3092-
// Only add --LogLevel when user explicitly specified it via CLI.
3102+
minimumLogLevel = (LogLevel)logLevel;
3103+
// Only add --log-level when user explicitly specified it via CLI.
30933104
// This allows MCP logging/setLevel to work when no CLI override is present.
3094-
args.Add("--LogLevel");
3105+
args.Add("--log-level");
30953106
args.Add(minimumLogLevel.ToString());
30963107
}
30973108
else
@@ -3100,7 +3111,7 @@ public static bool TryStartEngineWithOptions(StartOptions options, FileSystemRun
31003111

31013112
// Track whether config explicitly set a log level. In MCP stdio mode this
31023113
// allows CLI logs to be emitted to stderr (instead of being suppressed)
3103-
// when the user expressed intent via the config file rather than --LogLevel.
3114+
// when the user expressed intent via the config file rather than --log-level.
31043115
if (deserializedRuntimeConfig.HasExplicitLogLevel())
31053116
{
31063117
Utils.IsConfigOverriding = true;

src/Cli/CustomLoggerProvider.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ public class CustomConsoleLogger : ILogger
2828
private readonly LogLevel _minimumLogLevel;
2929

3030
// Minimum LogLevel for CLI output.
31-
// For MCP mode: prefer CLI's --LogLevel, fall back to config's log-level, otherwise suppress all.
31+
// For MCP mode: prefer CLI's --log-level, fall back to config's log-level, otherwise suppress all.
3232
// For non-MCP mode: always use the level passed to the constructor.
33-
// Note: --LogLevel is meant for the ENGINE's log level, not CLI's output.
33+
// Note: --log-level is meant for the ENGINE's log level, not CLI's output.
3434
public CustomConsoleLogger(LogLevel minimumLogLevel = LogLevel.Information)
3535
{
3636
_minimumLogLevel = Cli.Utils.IsMcpStdioMode
@@ -93,13 +93,13 @@ public CustomConsoleLogger(LogLevel minimumLogLevel = LogLevel.Information)
9393
/// <summary>
9494
/// Creates Log message by setting console message color based on LogLevel.
9595
/// In MCP stdio mode:
96-
/// - If user explicitly set --LogLevel (CLI) or log-level (config): write to stderr (colored output)
96+
/// - If user explicitly set --log-level (CLI) or log-level (config): write to stderr (colored output)
9797
/// - Otherwise: suppress entirely to keep stdout clean for JSON-RPC protocol.
9898
/// </summary>
9999
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
100100
{
101101
// In MCP stdio mode, only output logs if user explicitly requested a log level
102-
// via either the CLI --LogLevel flag or the runtime config file's log-level.
102+
// via either the CLI --log-level flag or the runtime config file's log-level.
103103
// In that case, write to stderr to keep stdout clean for JSON-RPC.
104104
if (Cli.Utils.IsMcpStdioMode)
105105
{

src/Cli/Exporter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ private static async Task ExportGraphQL(
116116
isHttpsRedirectionDisabled: false,
117117
config: options.Config!,
118118
mcpStdio: false,
119-
mcpRole: null);
119+
mcpRole: null,
120+
logLevelLegacy: null);
120121

121122
Task dabService = Task.Run(() =>
122123
{

src/Cli/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private static void ParseEarlyFlags(string[] args)
6060
{
6161
Utils.IsMcpStdioMode = true;
6262
}
63-
else if (string.Equals(arg, "--LogLevel", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
63+
else if (string.Equals(arg, "--log-level", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
6464
{
6565
Utils.IsCliOverriding = true;
6666
if (Enum.TryParse<LogLevel>(args[i + 1], ignoreCase: true, out LogLevel cliLogLevel))

src/Cli/Utils.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,21 @@ public class Utils
2929
public static bool IsMcpStdioMode { get; set; }
3030

3131
/// <summary>
32-
/// When true, the CLI is the source overriding the log level (i.e., <c>--LogLevel</c> was supplied).
32+
/// When true, the CLI is the source overriding the log level (i.e., <c>--log-level</c> was supplied).
3333
/// This allows logs to be written to stderr instead of being completely suppressed.
3434
/// </summary>
3535
public static bool IsCliOverriding { get; set; }
3636

3737
/// <summary>
38-
/// The log level specified via CLI --LogLevel flag.
38+
/// The log level specified via CLI --log-level flag.
3939
/// Only valid when IsCliOverriding is true.
4040
/// </summary>
4141
public static LogLevel CliLogLevel { get; set; } = LogLevel.Information;
4242

4343
/// <summary>
4444
/// When true, the runtime config is the source overriding the log level
4545
/// (i.e., <c>runtime.telemetry.log-level</c> was explicitly set).
46-
/// This allows CLI logs to be written to stderr in MCP mode even when no --LogLevel flag was provided.
46+
/// This allows CLI logs to be written to stderr in MCP mode even when no --log-level flag was provided.
4747
/// </summary>
4848
public static bool IsConfigOverriding { get; set; }
4949

src/Core/Telemetry/ILogLevelController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public interface ILogLevelController
1212
{
1313
/// <summary>
1414
/// Gets a value indicating whether the CLI is the source overriding the log level
15-
/// (i.e., <c>--LogLevel</c> was supplied). When true, runtime-config (hot-reload)
15+
/// (i.e., <c>--log-level</c> was supplied). When true, runtime-config (hot-reload)
1616
/// updates are ignored.
1717
/// </summary>
1818
bool IsCliOverriding { get; }
@@ -35,7 +35,7 @@ public interface ILogLevelController
3535
/// The MCP level string is mapped to the appropriate LogLevel.
3636
/// Log-level precedence (highest to lowest):
3737
/// 1. Agent (MCP <c>logging/setLevel</c>) — always wins.
38-
/// 2. CLI <c>--LogLevel</c> flag.
38+
/// 2. CLI <c>--log-level</c> flag.
3939
/// 3. Config <c>runtime.telemetry.log-level</c>.
4040
/// 4. Defaults.
4141
/// </summary>

0 commit comments

Comments
 (0)