winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit ad0337871964951de642bfc778bc558ee4190e00
parent 896424540b795753eba358b6405db203d907b4c5
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Fri,  8 Dec 2023 09:05:03 -0800

Allow user settings to control logging channels (#3955)

Add a user setting to control the enabled logging channels, then disable
the `SQL` by default. This channel is responsible for 95% of the log
lines in a verbose file, yet they are only helpful when debugging a very
specific set of issues that does not occur often.

Additionally, make the COM logging use the settings for logging (both
channels and level).
Diffstat:
M.github/actions/spelling/expect.txt | 1+
Mdoc/Settings.md | 23++++++++++++++++++++---
Mschemas/JSON/settings/settings.schema.0.2.json | 23+++++++++++++++++++++++
Msrc/AppInstallerCLICore/COMContext.cpp | 4++--
Msrc/AppInstallerCLICore/Core.cpp | 3+--
Msrc/AppInstallerCLITests/UserSettings.cpp | 46++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCommonCore/Public/winget/UserSettings.h | 2++
Msrc/AppInstallerCommonCore/UserSettings.cpp | 12++++++++++++
Msrc/AppInstallerSharedLib/AppInstallerLogging.cpp | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/AppInstallerSharedLib/Public/AppInstallerLogging.h | 29++++++++++++++++++-----------
10 files changed, 178 insertions(+), 44 deletions(-)

diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt @@ -40,6 +40,7 @@ BEFACEF BFirst bght BITMAPINFOHEADER +bitspace bkup blargle blockedbypolicy diff --git a/doc/Settings.md b/doc/Settings.md @@ -194,14 +194,31 @@ If set to true, the `telemetry.disable` setting will prevent any event from bein ## 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 +The `logging` settings control the level of detail in log files. ### level + `--verbose-logs` will override this setting and always creates a verbose log. +Defaults to `info` if value is not set or is invalid. + +```json + "logging": { + "level": "verbose" | "info" | "warning" | "error" | "critical" + }, +``` + +### channels + +The valid values in this array are defined in the function `GetChannelFromName` in the [logging code](../src/AppInstallerSharedLib/AppInstallerLogging.cpp). These align with the ***channel identifier*** found in the log files. For example, ***`CORE`*** in: +``` +2023-12-06 19:17:07.988 [CORE] WinGet, version [1.7.0-preview], activity [{24A91EA8-46BE-47A1-B65C-CEBCE90B8675}] +``` + +In addition, there are special values that cover multiple channels. `default` is the default set of channels, while `all` is all of the channels. Invalid values are ignored. + ```json "logging": { - "level": ["verbose", "info", "warning", "error", "critical"] + "channels": ["default"] }, ``` diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json @@ -50,6 +50,29 @@ "error", "critical" ] + }, + "channels": { + "description": "The logging channels to enable", + "type": "array", + "items": { + "uniqueItems": "true", + "type": "string", + "enum": [ + "fail", + "cli", + "sql", + "repo", + "yaml", + "core", + "test", + "config", + "default", + "all" + ], + "maxLength": 20 + }, + "minItems": 0, + "maxItems": 20 } } }, diff --git a/src/AppInstallerCLICore/COMContext.cpp b/src/AppInstallerCLICore/COMContext.cpp @@ -77,8 +77,8 @@ namespace AppInstaller::CLI::Execution void COMContext::SetLoggers() { - Logging::Log().SetLevel(Logging::Level::Info); - Logging::Log().EnableChannel(Logging::Channel::All); + Logging::Log().EnableChannel(Settings::User().Get<Settings::Setting::LoggingChannelPreference>()); + Logging::Log().SetLevel(Settings::User().Get<Settings::Setting::LoggingLevelPreference>()); // TODO: Log to file for COM API calls only when debugging in visual studio Logging::FileLogger::Add(s_comLogFileNamePrefix); diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp @@ -64,8 +64,7 @@ namespace AppInstaller::CLI Execution::Context context{ std::cout, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); - // Enable all logging for this phase; we will update once we have the arguments - Logging::Log().EnableChannel(Logging::Channel::All); + Logging::Log().EnableChannel(Settings::User().Get<Settings::Setting::LoggingChannelPreference>()); Logging::Log().SetLevel(Settings::User().Get<Settings::Setting::LoggingLevelPreference>()); Logging::FileLogger::Add(); Logging::EnableWilFailureTelemetry(); diff --git a/src/AppInstallerCLITests/UserSettings.cpp b/src/AppInstallerCLITests/UserSettings.cpp @@ -576,3 +576,49 @@ TEST_CASE("SettingsMaxResumes", "[settings]") REQUIRE(userSettingTest.Get<Setting::MaxResumes>() == 5); } } + +TEST_CASE("LoggingChannels", "[settings]") +{ + auto again = DeleteUserSettingsFiles(); + + SECTION("Not provided") + { + std::string_view json = R"({ })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get<Setting::LoggingChannelPreference>() == Channel::Defaults); + } + SECTION("No channels") + { + std::string_view json = R"({ "logging": { "channels": [] } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get<Setting::LoggingChannelPreference>() == Channel::None); + } + SECTION("Default") + { + std::string_view json = R"({ "logging": { "channels": ["default"] } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get<Setting::LoggingChannelPreference>() == Channel::Defaults); + } + SECTION("Multiple") + { + std::string_view json = R"({ "logging": { "channels": ["core","Repo","YAML"] } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get<Setting::LoggingChannelPreference>() == (Channel::Core | Channel::Repo | Channel::YAML)); + } + SECTION("Some invalid") + { + std::string_view json = R"({ "logging": { "channels": ["cli","sql","INVALID"] } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get<Setting::LoggingChannelPreference>() == (Channel::CLI | Channel::SQL)); + } +} diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -97,6 +97,7 @@ namespace AppInstaller::Settings NetworkWingetAlternateSourceURL, // Logging LoggingLevelPreference, + LoggingChannelPreference, // Uninstall behavior UninstallPurgePortablePackage, // Download behavior @@ -187,6 +188,7 @@ namespace AppInstaller::Settings #endif // Logging SETTINGMAPPING_SPECIALIZATION(Setting::LoggingLevelPreference, std::string, Logging::Level, Logging::Level::Info, ".logging.level"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::LoggingChannelPreference, std::vector<std::string>, Logging::Channel, Logging::Channel::Defaults, ".logging.channels"sv); // Interactivity SETTINGMAPPING_SPECIALIZATION(Setting::InteractivityDisable, bool, bool, false, ".interactivity.disable"sv); diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp @@ -435,6 +435,18 @@ namespace AppInstaller::Settings } return {}; } + + WINGET_VALIDATE_SIGNATURE(LoggingChannelPreference) + { + Logging::Channel result = Logging::Channel::None; + + for (auto const& entry : value) + { + result |= GetChannelFromName(entry); + } + + return result; + } } #ifndef AICLI_DISABLE_TEST_HOOKS diff --git a/src/AppInstallerSharedLib/AppInstallerLogging.cpp b/src/AppInstallerSharedLib/AppInstallerLogging.cpp @@ -8,28 +8,7 @@ namespace AppInstaller::Logging { - namespace - { - template <typename E> - std::underlying_type_t<E> AsNum(E e) - { - return static_cast<std::underlying_type_t<E>>(e); - } - - uint64_t ConvertChannelToBitmask(Channel channel) - { - if (channel == Channel::All) - { - return std::numeric_limits<uint64_t>::max(); - } - else - { - return (1ull << AsNum(channel)); - } - } - } - - char const* GetChannelName(Channel channel) + std::string_view GetChannelName(Channel channel) { switch(channel) { @@ -45,6 +24,54 @@ namespace AppInstaller::Logging } } + Channel GetChannelFromName(std::string_view channel) + { + std::string lowerChannel = Utility::ToLower(channel); + + if (lowerChannel == "fail") + { + return Channel::Fail; + } + else if (lowerChannel == "cli") + { + return Channel::CLI; + } + else if (lowerChannel == "sql") + { + return Channel::SQL; + } + else if (lowerChannel == "repo") + { + return Channel::Repo; + } + else if (lowerChannel == "yaml") + { + return Channel::YAML; + } + else if (lowerChannel == "core") + { + return Channel::Core; + } + else if (lowerChannel == "test") + { + return Channel::Test; + } + else if (lowerChannel == "conf" || lowerChannel == "config") + { + return Channel::Config; + } + else if (lowerChannel == "default" || lowerChannel == "defaults") + { + return Channel::Defaults; + } + else if (lowerChannel == "all") + { + return Channel::All; + } + + return Channel::None; + } + size_t GetMaxChannelNameLength() { return 4; } void DiagnosticLogger::AddLogger(std::unique_ptr<ILogger>&& logger) @@ -89,12 +116,12 @@ namespace AppInstaller::Logging void DiagnosticLogger::EnableChannel(Channel channel) { - m_enabledChannels |= ConvertChannelToBitmask(channel); + WI_SetAllFlags(m_enabledChannels, channel); } void DiagnosticLogger::DisableChannel(Channel channel) { - m_enabledChannels &= ~ConvertChannelToBitmask(channel); + WI_ClearAllFlags(m_enabledChannels, channel); } void DiagnosticLogger::SetLevel(Level level) @@ -110,8 +137,8 @@ namespace AppInstaller::Logging bool DiagnosticLogger::IsEnabled(Channel channel, Level level) const { return (!m_loggers.empty() && - (m_enabledChannels & ConvertChannelToBitmask(channel)) != 0 && - (AsNum(level) >= AsNum(m_enabledLevel))); + WI_IsAnyFlagSet(m_enabledChannels, channel) && + (ToIntegral(level) >= ToIntegral(m_enabledLevel))); } void DiagnosticLogger::Write(Channel channel, Level level, std::string_view message) diff --git a/src/AppInstallerSharedLib/Public/AppInstallerLogging.h b/src/AppInstallerSharedLib/Public/AppInstallerLogging.h @@ -48,19 +48,26 @@ namespace AppInstaller::Logging // Channels enable large groups of logs to be enabled or disabled together. enum class Channel : uint32_t { - Fail, - CLI, - SQL, - Repo, - YAML, - Core, - Test, - Config, - All, + Fail = 0x1, + CLI = 0x2, + SQL = 0x4, + Repo = 0x8, + YAML = 0x10, + Core = 0x20, + Test = 0x40, + Config = 0x80, + None = 0, + All = 0xFFFFFFFF, + Defaults = All & ~SQL, }; + DEFINE_ENUM_FLAG_OPERATORS(Channel); + // Gets the channel's name as a string. - char const* GetChannelName(Channel channel); + std::string_view GetChannelName(Channel channel); + + // Gets the channel from it's name. + Channel GetChannelFromName(std::string_view channel); // Gets the maximum channel name length in characters. size_t GetMaxChannelNameLength(); @@ -153,7 +160,7 @@ namespace AppInstaller::Logging private: std::vector<std::unique_ptr<ILogger>> m_loggers; - uint64_t m_enabledChannels = 0; + Channel m_enabledChannels = Channel::None; Level m_enabledLevel = Level::Info; };