diff --git a/doc/Settings.md b/doc/Settings.md index 8e463f7b28..b732139c61 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -106,6 +106,19 @@ See [details on telemetry](../README.md#datatelemetry), and our [primary privacy If set to true, the `telemetry.disable` setting will prevent any event from being written by the program. +## Logging + +The `logging` settings control the level of detail in log files. `--verbose-logs` will override this setting and always creates a verbose log. +Defaults to `info` if value is not set or is invalid + +### level + +```json + "logging": { + "level": ["verbose", "info", "warning", "error", "critical"] + }, +``` + ## Network The `network` settings influence how winget uses the network to retrieve packages and metadata. diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json index 2a9ab5bbd8..b839fae7db 100644 --- a/schemas/JSON/settings/settings.schema.0.2.json +++ b/schemas/JSON/settings/settings.schema.0.2.json @@ -31,6 +31,23 @@ } } }, + "Logging": { + "description": "Logging settings", + "type": "object", + "properties": { + "level": { + "description": "Preferred logging level", + "type": "string", + "enum": [ + "verbose", + "info", + "warning", + "error", + "critical" + ] + } + } + }, "InstallPrefReq": { "description": "Shared schema for preferences and requirements", "type": "object", @@ -150,6 +167,12 @@ }, "additionalItems": true }, + { + "properties": { + "logging": { "$ref": "#/definitions/Logging" } + }, + "additionalItems": true + }, { "properties": { "source": { "$ref": "#/definitions/Source" } diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index 9baa0b3936..ca7a47d6a3 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -66,7 +66,7 @@ namespace AppInstaller::CLI // Enable all logging for this phase; we will update once we have the arguments Logging::Log().EnableChannel(Logging::Channel::All); - Logging::Log().SetLevel(Logging::Level::Info); + Logging::Log().SetLevel(Settings::User().Get()); Logging::AddFileLogger(); Logging::EnableWilFailureTelemetry(); diff --git a/src/AppInstallerCLITests/UserSettings.cpp b/src/AppInstallerCLITests/UserSettings.cpp index 02cb75c6bb..dd3ed57d6a 100644 --- a/src/AppInstallerCLITests/UserSettings.cpp +++ b/src/AppInstallerCLITests/UserSettings.cpp @@ -5,6 +5,7 @@ #include "TestSettings.h" #include #include +#include "AppInstallerLogging.h" #include @@ -13,6 +14,7 @@ #include using namespace AppInstaller::Settings; +using namespace AppInstaller::Logging; using namespace AppInstaller::Runtime; using namespace TestCommon; using namespace std::string_literals; @@ -202,6 +204,82 @@ TEST_CASE("SettingProgressBar", "[settings]") } } +TEST_CASE("SettingLoggingLevelPreference", "[settings]") +{ + DeleteUserSettingsFiles(); + + SECTION("Default value") + { + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == Level::Info); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Info") + { + std::string_view json = R"({ "logging": { "level": "info" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == Level::Info); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Verbose") + { + std::string_view json = R"({ "logging": { "level": "verbose" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == Level::Verbose); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Warning") + { + std::string_view json = R"({ "logging": { "level": "warning" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == Level::Warning); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Error") + { + std::string_view json = R"({ "logging": { "level": "error" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == Level::Error); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Critical") + { + std::string_view json = R"({ "logging": { "level": "critical" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == Level::Crit); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Bad value") + { + std::string_view json = R"({ "logging": { "level": "fake" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == Level::Info); + REQUIRE(userSettingTest.GetWarnings().size() == 1); + } + SECTION("Bad value type") + { + std::string_view json = R"({ "logging": { "level": 5 } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == Level::Info); + REQUIRE(userSettingTest.GetWarnings().size() == 1); + } +} + TEST_CASE("SettingAutoUpdateIntervalInMinutes", "[settings]") { DeleteUserSettingsFiles(); diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 7cf95b2ec1..709a471b70 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -2,6 +2,7 @@ // Licensed under the MIT License. #pragma once #include "AppInstallerStrings.h" +#include "AppInstallerLogging.h" #include "winget/GroupPolicy.h" #include "winget/Resources.h" @@ -56,7 +57,6 @@ namespace AppInstaller::Settings DeliveryOptimization, }; - // Enum of settings. // Must start at 0 to enable direct access to variant in UserSettings. // Max must be last and unused. @@ -82,6 +82,7 @@ namespace AppInstaller::Settings InstallLocaleRequirement, EFDirectMSI, EnableSelfInitiatedMinidump, + LoggingLevelPreference, Max }; @@ -131,6 +132,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocaleRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFDirectMSI, bool, bool, false, ".experimentalFeatures.directMSI"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EnableSelfInitiatedMinidump, bool, bool, false, ".debugging.enableSelfInitiatedMinidump"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::LoggingLevelPreference, std::string, Logging::Level, Logging::Level::Info, ".logging.level"sv); // Used to deduce the SettingVariant type; making a variant that includes std::monostate and all SettingMapping types. template diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 8ea3b34585..110f2efe98 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -16,6 +16,7 @@ namespace AppInstaller::Settings using namespace std::string_view_literals; using namespace Runtime; using namespace Utility; + using namespace Logging; static constexpr std::string_view s_SettingEmpty = R"({ @@ -316,6 +317,38 @@ namespace AppInstaller::Settings { return std::chrono::seconds(value); } + + WINGET_VALIDATE_SIGNATURE(LoggingLevelPreference) + { + // logging preference possible values + static constexpr std::string_view s_logging_verbose = "verbose"; + static constexpr std::string_view s_logging_info = "info"; + static constexpr std::string_view s_logging_warning = "warning"; + static constexpr std::string_view s_logging_error = "error"; + static constexpr std::string_view s_logging_critical = "critical"; + + if (Utility::CaseInsensitiveEquals(value, s_logging_verbose)) + { + return Level::Verbose; + } + else if (Utility::CaseInsensitiveEquals(value, s_logging_info)) + { + return Level::Info; + } + else if (Utility::CaseInsensitiveEquals(value, s_logging_warning)) + { + return Level::Warning; + } + else if (Utility::CaseInsensitiveEquals(value, s_logging_error)) + { + return Level::Error; + } + else if (Utility::CaseInsensitiveEquals(value, s_logging_critical)) + { + return Level::Crit; + } + return {}; + } } #ifndef AICLI_DISABLE_TEST_HOOKS