Skip to content
Draft
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
40 changes: 40 additions & 0 deletions src/AppInstallerCLITests/UserSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,46 @@ TEST_CASE("SettingOutputSortDirection", "[settings]")
}
}

TEST_CASE("SettingLoggingUseCMTrace", "[settings]")
{
auto again = DeleteUserSettingsFiles();

SECTION("Default value")
{
UserSettingsTest userSettingTest;

REQUIRE(userSettingTest.Get<Setting::LoggingUseCMTrace>() == false);
REQUIRE(userSettingTest.GetWarnings().size() == 0);
}
SECTION("Enabled")
{
std::string_view json = R"({ "logging": { "useCMTrace": true } })";
SetSetting(Stream::PrimaryUserSettings, json);
UserSettingsTest userSettingTest;

REQUIRE(userSettingTest.Get<Setting::LoggingUseCMTrace>() == true);
REQUIRE(userSettingTest.GetWarnings().size() == 0);
}
SECTION("Disabled")
{
std::string_view json = R"({ "logging": { "useCMTrace": false } })";
SetSetting(Stream::PrimaryUserSettings, json);
UserSettingsTest userSettingTest;

REQUIRE(userSettingTest.Get<Setting::LoggingUseCMTrace>() == false);
REQUIRE(userSettingTest.GetWarnings().size() == 0);
}
SECTION("Bad value type")
{
std::string_view json = R"({ "logging": { "useCMTrace": "yes" } })";
SetSetting(Stream::PrimaryUserSettings, json);
UserSettingsTest userSettingTest;

REQUIRE(userSettingTest.Get<Setting::LoggingUseCMTrace>() == false);
REQUIRE(userSettingTest.GetWarnings().size() == 1);
}
}

TEST_CASE("ConvertToSortField", "[settings]")
{
SECTION("Valid values - lowercase")
Expand Down
57 changes: 56 additions & 1 deletion src/AppInstallerCommonCore/FileLogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,53 @@
return std::move(strstr).str();
}

// Formats a log line in CMTrace format.
// CMTrace log format: <![LOG[message]LOG]!><time="HH:mm:ss.fff+###" date="MM-dd-YYYY" component="channel" context="" type="N" thread="TID" file="">

Check failure on line 33 in src/AppInstallerCommonCore/FileLogger.cpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`fff` is not a recognized word (unrecognized-spelling)
std::string ToCMTraceLogLine(Channel channel, Level level, std::string_view message)
{
auto now = std::chrono::system_clock::now();
auto tt = std::chrono::system_clock::to_time_t(now);
tm localTime{};
_localtime64_s(&localTime, &tt);

auto sinceEpoch = now.time_since_epoch();
auto leftoverMillis = std::chrono::duration_cast<std::chrono::milliseconds>(sinceEpoch) - std::chrono::duration_cast<std::chrono::seconds>(sinceEpoch);

// Get UTC bias in minutes (positive means west of UTC, CMTrace uses positive for west)
long timezoneBiasSeconds = 0;
_get_timezone(&timezoneBiasSeconds);
long biasMins = timezoneBiasSeconds / 60;

// CMTrace type: 1=Info/Verbose, 2=Warning, 3=Error/Critical
int type;
switch (level)
{
case Level::Warning: type = 2; break;
case Level::Error:
case Level::Crit: type = 3; break;
default: type = 1; break;
}

std::stringstream strstr;
strstr << "<![LOG[" << message << "]LOG]!>"
<< "<time=\""
<< std::setw(2) << std::setfill('0') << localTime.tm_hour << ":"
<< std::setw(2) << std::setfill('0') << localTime.tm_min << ":"
<< std::setw(2) << std::setfill('0') << localTime.tm_sec << "."
<< std::setw(3) << std::setfill('0') << leftoverMillis.count()
<< "+" << biasMins << "\""
<< " date=\""
<< std::setw(2) << std::setfill('0') << (1 + localTime.tm_mon) << "-"
<< std::setw(2) << std::setfill('0') << localTime.tm_mday << "-"
<< (1900 + localTime.tm_year) << "\""
<< " component=\"" << GetChannelName(channel) << "\""
<< " context=\"\""
<< " type=\"" << type << "\""
<< " thread=\"" << GetCurrentThreadId() << "\""
<< " file=\"\">";
return std::move(strstr).str();
}

// Determines the difference between the given position and the maximum as an offset.
std::ofstream::off_type CalculateDiff(const std::ofstream::pos_type& position, std::ofstream::off_type maximum)
{
Expand Down Expand Up @@ -94,7 +141,15 @@

void FileLogger::Write(Channel channel, Level level, std::string_view message) noexcept try
{
std::string log = ToLogLine(channel, level, message);
std::string log;
if (Settings::User().Get<Settings::Setting::LoggingUseCMTrace>())
{
log = ToCMTraceLogLine(channel, level, message);
}
else
{
log = ToLogLine(channel, level, message);
}
WriteDirect(channel, level, log);
}
catch (...) {}
Expand Down
2 changes: 2 additions & 0 deletions src/AppInstallerCommonCore/Public/winget/UserSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ namespace AppInstaller::Settings
LoggingFileTotalSizeLimitInMB,
LoggingFileIndividualSizeLimitInMB,
LoggingFileCountLimit,
LoggingUseCMTrace,
// Uninstall behavior
UninstallPurgePortablePackage,
// Download behavior
Expand Down Expand Up @@ -237,6 +238,7 @@ namespace AppInstaller::Settings
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileTotalSizeLimitInMB, uint32_t, uint32_t, 128, ".logging.file.totalSizeLimitInMB"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileIndividualSizeLimitInMB, uint32_t, uint32_t, 16, ".logging.file.individualSizeLimitInMB"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileCountLimit, uint32_t, uint32_t, 0, ".logging.file.countLimit"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingUseCMTrace, bool, bool, false, ".logging.useCMTrace"sv);
// Interactivity
SETTINGMAPPING_SPECIALIZATION(Setting::InteractivityDisable, bool, bool, false, ".interactivity.disable"sv);
// Output behavior
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCommonCore/UserSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ namespace AppInstaller::Settings
WINGET_VALIDATE_PASS_THROUGH(LoggingFileTotalSizeLimitInMB)
WINGET_VALIDATE_PASS_THROUGH(LoggingFileIndividualSizeLimitInMB)
WINGET_VALIDATE_PASS_THROUGH(LoggingFileCountLimit)
WINGET_VALIDATE_PASS_THROUGH(LoggingUseCMTrace)

#ifndef AICLI_DISABLE_TEST_HOOKS
WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump)
Expand Down
Loading