Skip to content

Allow for the UserLogDefaultLogLevel to be set independent of system logs #4318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ internal class StartHostAction : BaseAction

public string? HostRuntime { get; set; }

public string UserLogLevel { get; set; }

public StartHostAction(ISecretsManager secretsManager, IProcessManager processManager)
{
_secretsManager = secretsManager;
Expand Down Expand Up @@ -178,6 +180,11 @@ public override ICommandLineParserResult ParseArgs(string[] args)
.WithDescription($"If provided, determines which version of the host to start. Allowed values are '{DotnetConstants.InProc6HostRuntime}', '{DotnetConstants.InProc8HostRuntime}', and 'default' (which runs the out-of-process host).")
.Callback(startHostFromRuntime => HostRuntime = startHostFromRuntime);

Parser
.Setup<string>("userLogLevel")
.WithDescription($"If provided, determines the loglevel of user logs. Allowed values are '{LogLevel.Trace}', '{LogLevel.Debug}', '{LogLevel.Information}', '{LogLevel.Warning}', '{LogLevel.Error}', '{LogLevel.Critical}' and '{LogLevel.None}'.")
.Callback(userLogLevel => UserLogLevel = userLogLevel);

var parserResult = base.ParseArgs(args);
bool verboseLoggingArgExists = parserResult.UnMatchedOptions.Any(o => o.LongName.Equals("verbose", StringComparison.OrdinalIgnoreCase));
// Input args do not contain --verbose flag
Expand All @@ -190,7 +197,7 @@ public override ICommandLineParserResult ParseArgs(string[] args)

private async Task<IWebHost> BuildWebHost(ScriptApplicationHostOptions hostOptions, Uri listenAddress, Uri baseAddress, X509Certificate2 certificate)
{
LoggingFilterHelper loggingFilterHelper = new LoggingFilterHelper(_hostJsonConfig, VerboseLogging);
LoggingFilterHelper loggingFilterHelper = new LoggingFilterHelper(_hostJsonConfig, VerboseLogging, UserLogLevel);
if (GlobalCoreToolsSettings.CurrentWorkerRuntime == WorkerRuntime.dotnet ||
GlobalCoreToolsSettings.CurrentWorkerRuntime == WorkerRuntime.dotnetIsolated)
{
Expand Down
21 changes: 19 additions & 2 deletions src/Azure.Functions.Cli/Diagnostics/LoggingFilterHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ public class LoggingFilterHelper
public const string Ci_Continuous_Integration = "CONTINUOUS_INTEGRATION"; // Travis CI, Cirrus CI
public const string Ci_Build_Number = "BUILD_NUMBER"; // Travis CI, Cirrus CI
public const string Ci_Run_Id = "RUN_ID"; // TaskCluster, dsari
public static readonly string[] ValidUserLogLevels = ["Trace", "Debug", "Information", "Warning", "Error","Critical", "None"];

public LoggingFilterHelper(IConfigurationRoot hostJsonConfig, bool? verboseLogging)
public LoggingFilterHelper(IConfigurationRoot hostJsonConfig, bool? verboseLogging, string userLogLevel = null)
{
VerboseLogging = verboseLogging.HasValue && verboseLogging.Value;

Expand All @@ -34,7 +35,16 @@ public LoggingFilterHelper(IConfigurationRoot hostJsonConfig, bool? verboseLoggi
if (Utilities.LogLevelExists(hostJsonConfig, Utilities.LogLevelDefaultSection, out LogLevel logLevel))
{
SystemLogDefaultLogLevel = logLevel;
UserLogDefaultLogLevel = logLevel;
}

// Check for user log level
if (!string.IsNullOrEmpty(userLogLevel))
{
ValidateUserLogLevel(userLogLevel);
if (Enum.TryParse(userLogLevel, true, out LogLevel UserLogLevel))
{
UserLogDefaultLogLevel = UserLogLevel;
}
}
}

Expand Down Expand Up @@ -68,5 +78,12 @@ internal bool IsCiEnvironment(bool verboseLoggingArgExists)
}
return false;
}
private void ValidateUserLogLevel(string UserLogLevel)
{
if (LoggingFilterHelper.ValidUserLogLevels.Contains(UserLogLevel, StringComparer.OrdinalIgnoreCase) == false)
{
throw new CliException($"The userLogLevel value provided, '{UserLogLevel}', is invalid. Valid values are '{string.Join("', '", LoggingFilterHelper.ValidUserLogLevels)}'.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add that the default level for user logs is Information

}
}
}
}
74 changes: 74 additions & 0 deletions test/Azure.Functions.Cli.Tests/E2E/StartTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1857,6 +1857,80 @@ await CliTester.Run(new RunConfiguration[]
}
}

[Theory]
[InlineData("dotnet-isolated", "debug")]
[InlineData("dotnet", "debug")]
public async Task Start_Host_SpecifieduserLogLevel_SuccessfulFunctionExecution(string WorkerRuntime, string UserLogLevel)
{
await CliTester.Run(new RunConfiguration[]
{
new RunConfiguration
{
Commands = new[]
{
$"init . --worker-runtime {WorkerRuntime}",
$"new --template Httptrigger --name HttpTrigger",
},
CommandTimeout = TimeSpan.FromSeconds(300),
},
new RunConfiguration
{
Commands = new[]
{
$"start --port {_funcHostPort} --userLogLevel {UserLogLevel}"
},
ExpectExit = false,
Test = async (workingDir, p, _) =>
{
using (var client = new HttpClient() { BaseAddress = new Uri($"http://localhost:{_funcHostPort}") })
{
(await WaitUntilReady(client)).Should().BeTrue(because: _serverNotReady);
var response = await client.GetAsync("/api/HttpTriggerFunc?name=Test");
response.StatusCode.Should().Be(HttpStatusCode.OK);
await Task.Delay(TimeSpan.FromSeconds(2));
p.Kill();
}
},
CommandTimeout = TimeSpan.FromSeconds(100),
},
}, _output);
}

[Theory]
[InlineData("TestLogLevel")]
public async Task Start_Host_Validate_SpecifieduserLogLevel( string UserLogLevel)
{
await CliTester.Run(new RunConfiguration[]
{
new RunConfiguration
{
Commands = new[]
{
"init . --worker-runtime dotnet-isolated",
"new --template Httptrigger --name HttpTrigger",
}
},
new RunConfiguration
{
Commands = new[]
{
$"start --port {_funcHostPort} --userLogLevel {UserLogLevel}"
},
ExpectExit = true,
ExitInError = true,
ErrorContains = [$"The userLogLevel value provided, '{UserLogLevel}', is invalid. Valid values are '{string.Join("', '", LoggingFilterHelper.ValidUserLogLevels)}'."],
Test = async (workingDir, p, _) =>
{
using (var client = new HttpClient() { BaseAddress = new Uri($"http://localhost:{_funcHostPort}") })
{
await Task.Delay(TimeSpan.FromSeconds(2));
}
},
CommandTimeout = TimeSpan.FromSeconds(100),
},
}, _output);
}

private async Task<bool> WaitUntilReady(HttpClient client)
{
for (var limit = 0; limit < 10; limit++)
Expand Down
Loading