Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8d26aec
Fix Setting Azure.DataApiBuilder to none bug
RubenCerna2079 Mar 16, 2026
9d21e65
Fix --loglevel flag
RubenCerna2079 Mar 17, 2026
3bb69d5
Fix bugs
RubenCerna2079 Mar 17, 2026
d0cb93a
Fix bugs log level
RubenCerna2079 Mar 17, 2026
d87ef7b
Fix log level bugs
RubenCerna2079 Mar 25, 2026
100914f
Merge branch 'main' into dev/rubencerna/fix-loglevel-bugs
RubenCerna2079 Mar 25, 2026
2e6d2bf
Fix log buffer so it is only applied when using start option
RubenCerna2079 Mar 26, 2026
741d2d4
Merge branch 'main' into dev/rubencerna/fix-loglevel-bugs
RubenCerna2079 Mar 26, 2026
260a9c0
Add missing comments
RubenCerna2079 Mar 27, 2026
c460cab
Fix syntax errors
RubenCerna2079 Mar 27, 2026
c487a5d
Fix syntax errors
RubenCerna2079 Mar 27, 2026
29818d9
Changes based on comments
RubenCerna2079 Mar 27, 2026
e1ee165
Changes based on comments
RubenCerna2079 Apr 2, 2026
5832465
Fix merge conflict
RubenCerna2079 Apr 2, 2026
0e64146
Changes based on comments
RubenCerna2079 Apr 4, 2026
ddd6508
Merge branch 'main' into dev/rubencerna/fix-loglevel-bugs
Aniruddh25 Apr 4, 2026
85d8381
Changes to CLI section
RubenCerna2079 Apr 9, 2026
0ca53e7
Fix cli
RubenCerna2079 Apr 9, 2026
7101d4a
Merge remote-tracking branch 'refs/remotes/origin/dev/rubencerna/fix-…
RubenCerna2079 Apr 14, 2026
4c981c9
Merge branch 'main' into dev/rubencerna/fix-loglevel-bugs
RubenCerna2079 Apr 14, 2026
4a331eb
Remove unnecessary function
RubenCerna2079 Apr 15, 2026
f580686
Fix syntax errors
RubenCerna2079 Apr 15, 2026
6a8bd36
Merge branch 'main' into dev/rubencerna/fix-loglevel-bugs
RubenCerna2079 Apr 15, 2026
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
45 changes: 33 additions & 12 deletions src/Cli.Tests/EndToEndTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -821,24 +821,12 @@ public Task TestUpdatingStoredProcedureWithRestMethods()
[DataRow("--LogLevel 0", DisplayName = "LogLevel 0 from command line.")]
[DataRow("--LogLevel 1", DisplayName = "LogLevel 1 from command line.")]
[DataRow("--LogLevel 2", DisplayName = "LogLevel 2 from command line.")]
[DataRow("--LogLevel 3", DisplayName = "LogLevel 3 from command line.")]
[DataRow("--LogLevel 4", DisplayName = "LogLevel 4 from command line.")]
[DataRow("--LogLevel 5", DisplayName = "LogLevel 5 from command line.")]
[DataRow("--LogLevel 6", DisplayName = "LogLevel 6 from command line.")]
[DataRow("--LogLevel Trace", DisplayName = "LogLevel Trace from command line.")]
[DataRow("--LogLevel Debug", DisplayName = "LogLevel Debug from command line.")]
[DataRow("--LogLevel Information", DisplayName = "LogLevel Information from command line.")]
[DataRow("--LogLevel Warning", DisplayName = "LogLevel Warning from command line.")]
[DataRow("--LogLevel Error", DisplayName = "LogLevel Error from command line.")]
[DataRow("--LogLevel Critical", DisplayName = "LogLevel Critical from command line.")]
[DataRow("--LogLevel None", DisplayName = "LogLevel None from command line.")]
[DataRow("--LogLevel tRace", DisplayName = "Case sensitivity: LogLevel Trace from command line.")]
[DataRow("--LogLevel DebUG", DisplayName = "Case sensitivity: LogLevel Debug from command line.")]
[DataRow("--LogLevel information", DisplayName = "Case sensitivity: LogLevel Information from command line.")]
[DataRow("--LogLevel waRNing", DisplayName = "Case sensitivity: LogLevel Warning from command line.")]
[DataRow("--LogLevel eRROR", DisplayName = "Case sensitivity: LogLevel Error from command line.")]
[DataRow("--LogLevel CrItIcal", DisplayName = "Case sensitivity: LogLevel Critical from command line.")]
[DataRow("--LogLevel NONE", DisplayName = "Case sensitivity: LogLevel None from command line.")]
public void TestEngineStartUpWithVerboseAndLogLevelOptions(string logLevelOption)
{
_fileSystem!.File.WriteAllText(TEST_RUNTIME_CONFIG_FILE, INITIAL_CONFIG);
Expand All @@ -857,6 +845,39 @@ public void TestEngineStartUpWithVerboseAndLogLevelOptions(string logLevelOption
StringAssert.Contains(output, $"User provided config file: {TEST_RUNTIME_CONFIG_FILE}", StringComparison.Ordinal);
}

/// <summary>
/// Test to validate that the engine starts successfully when --LogLevel is set to Warning
/// or above. At these levels, CLI phase messages (logged at Information) are suppressed,
/// so no stdout output is expected during the CLI phase.
/// </summary>
/// <param name="logLevelOption">Log level options</param>
[DataTestMethod]
[DataRow("--LogLevel 3", DisplayName = "LogLevel 3 from command line.")]
[DataRow("--LogLevel 4", DisplayName = "LogLevel 4 from command line.")]
[DataRow("--LogLevel 5", DisplayName = "LogLevel 5 from command line.")]
[DataRow("--LogLevel 6", DisplayName = "LogLevel 6 from command line.")]
[DataRow("--LogLevel Warning", DisplayName = "LogLevel Warning from command line.")]
[DataRow("--LogLevel Error", DisplayName = "LogLevel Error from command line.")]
[DataRow("--LogLevel Critical", DisplayName = "LogLevel Critical from command line.")]
[DataRow("--LogLevel None", DisplayName = "LogLevel None from command line.")]
[DataRow("--LogLevel waRNing", DisplayName = "Case sensitivity: LogLevel Warning from command line.")]
[DataRow("--LogLevel eRROR", DisplayName = "Case sensitivity: LogLevel Error from command line.")]
[DataRow("--LogLevel CrItIcal", DisplayName = "Case sensitivity: LogLevel Critical from command line.")]
[DataRow("--LogLevel NONE", DisplayName = "Case sensitivity: LogLevel None from command line.")]
public void TestEngineStartUpWithHighLogLevelOptions(string logLevelOption)
{
_fileSystem!.File.WriteAllText(TEST_RUNTIME_CONFIG_FILE, INITIAL_CONFIG);

using Process process = ExecuteDabCommand(
command: $"start --config {TEST_RUNTIME_CONFIG_FILE}",
logLevelOption
);

string? output = process.StandardOutput.ReadLine();
Assert.IsNull(output);
process.Kill();
}

/// <summary>
/// Validates that valid usage of verbs and associated options produce exit code 0 (CliReturnCode.SUCCESS).
/// Verifies that explicitly implemented verbs (add, update, init, start) and appropriately
Expand Down
7 changes: 5 additions & 2 deletions src/Cli.Tests/UtilsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,11 @@ public void TestMergeConfig()

FileSystemRuntimeConfigLoader loader = new(fileSystem);

LogBuffer logBuffer = new();

Environment.SetEnvironmentVariable(RUNTIME_ENVIRONMENT_VAR_NAME, "Test");

Assert.IsTrue(ConfigMerger.TryMergeConfigsIfAvailable(fileSystem, loader, new StringLogger(), out string? mergedConfig), "Failed to merge config files");
Assert.IsTrue(ConfigMerger.TryMergeConfigsIfAvailable(fileSystem, loader, new StringLogger(), logBuffer, out string? mergedConfig), "Failed to merge config files");
Assert.AreEqual(mergedConfig, "dab-config.Test.merged.json");
Assert.IsTrue(fileSystem.File.Exists(mergedConfig));
Assert.IsTrue(JToken.DeepEquals(JObject.Parse(MERGED_CONFIG), JObject.Parse(fileSystem.File.ReadAllText(mergedConfig))));
Expand Down Expand Up @@ -306,10 +308,11 @@ public void TestMergeConfigAvailability(
}

FileSystemRuntimeConfigLoader loader = new(fileSystem);
LogBuffer logBuffer = new();

Assert.AreEqual(
expectedIsMergedConfigAvailable,
ConfigMerger.TryMergeConfigsIfAvailable(fileSystem, loader, new StringLogger(), out string? mergedConfigFile),
ConfigMerger.TryMergeConfigsIfAvailable(fileSystem, loader, new StringLogger(), logBuffer, out string? mergedConfigFile),
"Availability of merge config should match");
Assert.AreEqual(expectedMergedConfigFileName, mergedConfigFile, "Merge config file name should match expected");

Expand Down
9 changes: 8 additions & 1 deletion src/Cli/Commands/StartOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class StartOptions : Options
{
private const string LOGLEVEL_HELPTEXT = "Specifies logging level as provided value. For possible values, see: https://go.microsoft.com/fwlink/?linkid=2263106";

public LogBuffer CliBuffer { get; }

public StartOptions(bool verbose, LogLevel? logLevel, bool isHttpsRedirectionDisabled, bool mcpStdio, string? mcpRole, string config)
: base(config)
{
Expand All @@ -27,6 +29,7 @@ public StartOptions(bool verbose, LogLevel? logLevel, bool isHttpsRedirectionDis
IsHttpsRedirectionDisabled = isHttpsRedirectionDisabled;
McpStdio = mcpStdio;
McpRole = mcpRole;
CliBuffer = new LogBuffer();
}

// SetName defines mutually exclusive sets, ie: can not have
Expand All @@ -48,11 +51,15 @@ public StartOptions(bool verbose, LogLevel? logLevel, bool isHttpsRedirectionDis

public int Handler(ILogger logger, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem)
Comment thread
RubenCerna2079 marked this conversation as resolved.
{
logger.LogInformation("{productName} {version}", PRODUCT_NAME, ProductInfo.GetProductVersion());
CliBuffer.BufferLog(Microsoft.Extensions.Logging.LogLevel.Information, $"{PRODUCT_NAME} {ProductInfo.GetProductVersion()}");
bool isSuccess = ConfigGenerator.TryStartEngineWithOptions(this, loader, fileSystem);

if (!isSuccess)
{
// Update loggers and flush buffers to ensure that all the logs are printed if the TryStartEngineWithOptions fails.
logger = Utils.LoggerFactoryForCli.CreateLogger<Program>();
CliBuffer.FlushToLogger(logger);

logger.LogError("Failed to start the engine{mode}.",
McpStdio ? " in MCP stdio mode" : string.Empty);
}
Expand Down
57 changes: 43 additions & 14 deletions src/Cli/ConfigGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2555,7 +2555,7 @@ public static bool VerifyCanUpdateRelationship(RuntimeConfig runtimeConfig, stri
/// </summary>
public static bool TryStartEngineWithOptions(StartOptions options, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem)
{
if (!TryGetConfigForRuntimeEngine(options.Config, loader, fileSystem, out string runtimeConfigFile))
if (!TryGetConfigForRuntimeEngine(options.Config, loader, fileSystem, out string runtimeConfigFile, options.CliBuffer))
{
return false;
}
Expand All @@ -2564,17 +2564,17 @@ public static bool TryStartEngineWithOptions(StartOptions options, FileSystemRun
// Replaces all the environment variables while deserializing when starting DAB.
if (!loader.TryLoadKnownConfig(out RuntimeConfig? deserializedRuntimeConfig, replaceEnvVar: true))
{
_logger.LogError("Failed to parse the config file: {runtimeConfigFile}.", runtimeConfigFile);
options.CliBuffer.BufferLog(LogLevel.Error, $"Failed to parse the config file: {runtimeConfigFile}.");
return false;
}
else
{
_logger.LogInformation("Loaded config file: {runtimeConfigFile}", runtimeConfigFile);
options.CliBuffer.BufferLog(LogLevel.Information, $"Loaded config file: {runtimeConfigFile}");
}

if (string.IsNullOrWhiteSpace(deserializedRuntimeConfig.DataSource.ConnectionString))
{
_logger.LogError("Invalid connection-string provided in the config.");
options.CliBuffer.BufferLog(LogLevel.Error, "Invalid connection-string provided in the config.");
return false;
}

Expand All @@ -2589,23 +2589,36 @@ public static bool TryStartEngineWithOptions(StartOptions options, FileSystemRun
{
if (options.LogLevel is < LogLevel.Trace or > LogLevel.None)
{
_logger.LogError(
"LogLevel's valid range is 0 to 6, your value: {logLevel}, see: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.loglevel",
options.LogLevel);
options.CliBuffer.BufferLog(LogLevel.Error,
$"LogLevel's valid range is 0 to 6, your value: {options.LogLevel}, see: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.loglevel");
return false;
}

minimumLogLevel = (LogLevel)options.LogLevel;
_logger.LogInformation("Setting minimum LogLevel: {minimumLogLevel}.", minimumLogLevel);
options.CliBuffer.BufferLog(LogLevel.Information, $"Setting minimum LogLevel: {minimumLogLevel}.");
}
else
{
minimumLogLevel = deserializedRuntimeConfig.GetConfiguredLogLevel();
HostMode hostModeType = deserializedRuntimeConfig.IsDevelopmentMode() ? HostMode.Development : HostMode.Production;

_logger.LogInformation($"Setting default minimum LogLevel: {minimumLogLevel} for {hostModeType} mode.", minimumLogLevel, hostModeType);
options.CliBuffer.BufferLog(LogLevel.Information, $"Setting default minimum LogLevel: {minimumLogLevel} for {hostModeType} mode.");
}

Utils.LoggerFactoryForCli = Utils.GetLoggerFactoryForCli(minimumLogLevel);

// Update logger for StartOptions
ILogger<Program> programLogger = Utils.LoggerFactoryForCli.CreateLogger<Program>();
options.CliBuffer.FlushToLogger(programLogger);

// Update logger for Utils
ILogger<Utils> utilsLogger = Utils.LoggerFactoryForCli.CreateLogger<Utils>();
Utils.SetCliUtilsLogger(utilsLogger);

// Update logger for ConfigGenerator
ILogger<ConfigGenerator> configGeneratorLogger = Utils.LoggerFactoryForCli.CreateLogger<ConfigGenerator>();
SetLoggerForCliConfigGenerator(configGeneratorLogger);

args.Add("--LogLevel");
args.Add(minimumLogLevel.ToString());

Expand Down Expand Up @@ -2696,16 +2709,32 @@ public static bool TryGetConfigForRuntimeEngine(
string? configToBeUsed,
FileSystemRuntimeConfigLoader loader,
IFileSystem fileSystem,
out string runtimeConfigFile)
out string runtimeConfigFile,
LogBuffer? logBuffer = null)
{
if (string.IsNullOrEmpty(configToBeUsed) && ConfigMerger.TryMergeConfigsIfAvailable(fileSystem, loader, _logger, out configToBeUsed))
if (string.IsNullOrEmpty(configToBeUsed) && ConfigMerger.TryMergeConfigsIfAvailable(fileSystem, loader, _logger, logBuffer, out configToBeUsed))
{
_logger.LogInformation("Using merged config file based on environment: {configToBeUsed}.", configToBeUsed);
if (logBuffer is null)
{
_logger.LogInformation("Using merged config file based on environment,: {configToBeUsed}.", configToBeUsed);
}
else
{
logBuffer.BufferLog(LogLevel.Information, $"Merged config file based on environment is available: {configToBeUsed}.");
}
}

if (!TryGetConfigFileBasedOnCliPrecedence(loader, configToBeUsed, out runtimeConfigFile))
if (!TryGetConfigFileBasedOnCliPrecedence(loader, configToBeUsed, out runtimeConfigFile, logBuffer))
{
_logger.LogError("Config not provided and default config file doesn't exist.");
if (logBuffer is null)
{
_logger.LogError("Config not provided and default config file doesn't exist.");
}
else
{
logBuffer.BufferLog(LogLevel.Error, "Config not provided and default config file doesn't exist.");
}

return false;
}

Expand Down
34 changes: 30 additions & 4 deletions src/Cli/ConfigMerger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static class ConfigMerger
/// and create a merged file called dab-config.{DAB_ENVIRONMENT}.merged.json
/// </summary>
/// <returns>Returns the name of the merged Config if successful.</returns>
public static bool TryMergeConfigsIfAvailable(IFileSystem fileSystem, FileSystemRuntimeConfigLoader loader, ILogger logger, out string? mergedConfigFile)
public static bool TryMergeConfigsIfAvailable(IFileSystem fileSystem, FileSystemRuntimeConfigLoader loader, ILogger logger, LogBuffer? cliBuffer, out string? mergedConfigFile)
{
string? environmentValue = Environment.GetEnvironmentVariable(FileSystemRuntimeConfigLoader.RUNTIME_ENVIRONMENT_VAR_NAME);
mergedConfigFile = null;
Expand All @@ -32,16 +32,42 @@ public static bool TryMergeConfigsIfAvailable(IFileSystem fileSystem, FileSystem
string overrideConfigJson = fileSystem.File.ReadAllText(environmentBasedConfigFile);

string currentDir = fileSystem.Directory.GetCurrentDirectory();
logger.LogInformation("Merging {baseFilePath} and {envFilePath}", Path.Combine(currentDir, baseConfigFile), Path.Combine(currentDir, environmentBasedConfigFile));

if (cliBuffer is null)
{
logger.LogInformation("Merging {baseFilePath} and {envFilePath}", Path.Combine(currentDir, baseConfigFile), Path.Combine(currentDir, environmentBasedConfigFile));
}
else
{
cliBuffer.BufferLog(LogLevel.Information, $"Merging {Path.Combine(currentDir, baseConfigFile)} and {Path.Combine(currentDir, environmentBasedConfigFile)}");
}

string mergedConfigJson = MergeJsonProvider.Merge(baseConfigJson, overrideConfigJson);
mergedConfigFile = FileSystemRuntimeConfigLoader.GetMergedFileNameForEnvironment(FileSystemRuntimeConfigLoader.CONFIGFILE_NAME, environmentValue);
fileSystem.File.WriteAllText(mergedConfigFile, mergedConfigJson);
logger.LogInformation("Generated merged config file: {mergedFile}", Path.Combine(currentDir, mergedConfigFile));

if (cliBuffer is null)
{
logger.LogInformation("Generated merged config file: {mergedFile}", Path.Combine(currentDir, mergedConfigFile));
}
else
{
cliBuffer.BufferLog(LogLevel.Information, $"Generated merged config file: {Path.Combine(currentDir, mergedConfigFile)}");
}

return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to merge the config files.");
if (cliBuffer is null)
{
logger.LogError(ex, "Failed to merge the config files.");
}
else
{
cliBuffer.BufferLog(LogLevel.Error, "Failed to merge the config files.", ex);
}

mergedConfigFile = null;
return false;
}
Expand Down
21 changes: 17 additions & 4 deletions src/Cli/CustomLoggerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,30 @@
/// </summary>
public class CustomLoggerProvider : ILoggerProvider
{
private readonly LogLevel _minimumLogLevel;

public CustomLoggerProvider(LogLevel minimumLogLevel = LogLevel.Information)
{
_minimumLogLevel = minimumLogLevel;
}

public void Dispose() { }

/// <inheritdoc/>
public ILogger CreateLogger(string categoryName)
{
return new CustomConsoleLogger();
return new CustomConsoleLogger(_minimumLogLevel);
}

public class CustomConsoleLogger : ILogger
{
// Minimum LogLevel. LogLevel below this would be disabled.
private readonly LogLevel _minimumLogLevel = LogLevel.Information;
private readonly LogLevel _minimumLogLevel;

public CustomConsoleLogger(LogLevel minimumLogLevel = LogLevel.Information)
{
_minimumLogLevel = minimumLogLevel;
}

// Color values based on LogLevel
// LogLevel Foreground Background
Expand Down Expand Up @@ -74,7 +86,7 @@ public class CustomConsoleLogger : ILogger
/// </summary>
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel) || logLevel < _minimumLogLevel)
if (!IsEnabled(logLevel))
{
return;
}
Expand All @@ -97,8 +109,9 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
/// <inheritdoc/>
public bool IsEnabled(LogLevel logLevel)
{
return true;
return logLevel != LogLevel.None && logLevel >= _minimumLogLevel;
Comment thread
RubenCerna2079 marked this conversation as resolved.
}

public IDisposable? BeginScope<TState>(TState state) where TState : notnull
{
throw new NotImplementedException();
Expand Down
2 changes: 1 addition & 1 deletion src/Cli/Exporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal class Exporter
public static bool Export(ExportOptions options, ILogger logger, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem)
{
// Attempt to locate the runtime configuration file based on CLI options
if (!TryGetConfigFileBasedOnCliPrecedence(loader, options.Config, out string runtimeConfigFile))
if (!TryGetConfigFileBasedOnCliPrecedence(loader: loader, userProvidedConfigFile: options.Config, runtimeConfigFile: out string runtimeConfigFile))
{
logger.LogError("Failed to find the config file provided, check your options and try again.");
return false;
Expand Down
Loading
Loading