Skip to content

Commit e01e440

Browse files
Copilotpl4nty
andauthored
Add LoggingUseCMTrace setting with CMTrace log format support and tests
Agent-Logs-Url: https://github.com/devicie/winget-cli/sessions/c4d184c0-b0f2-46bc-9e42-e68d558e7f8a Co-authored-by: pl4nty <21111317+pl4nty@users.noreply.github.com>
1 parent 3d4889d commit e01e440

4 files changed

Lines changed: 99 additions & 1 deletion

File tree

src/AppInstallerCLITests/UserSettings.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,46 @@ TEST_CASE("SettingOutputSortDirection", "[settings]")
925925
}
926926
}
927927

928+
TEST_CASE("SettingLoggingUseCMTrace", "[settings]")
929+
{
930+
auto again = DeleteUserSettingsFiles();
931+
932+
SECTION("Default value")
933+
{
934+
UserSettingsTest userSettingTest;
935+
936+
REQUIRE(userSettingTest.Get<Setting::LoggingUseCMTrace>() == false);
937+
REQUIRE(userSettingTest.GetWarnings().size() == 0);
938+
}
939+
SECTION("Enabled")
940+
{
941+
std::string_view json = R"({ "logging": { "useCMTrace": true } })";
942+
SetSetting(Stream::PrimaryUserSettings, json);
943+
UserSettingsTest userSettingTest;
944+
945+
REQUIRE(userSettingTest.Get<Setting::LoggingUseCMTrace>() == true);
946+
REQUIRE(userSettingTest.GetWarnings().size() == 0);
947+
}
948+
SECTION("Disabled")
949+
{
950+
std::string_view json = R"({ "logging": { "useCMTrace": false } })";
951+
SetSetting(Stream::PrimaryUserSettings, json);
952+
UserSettingsTest userSettingTest;
953+
954+
REQUIRE(userSettingTest.Get<Setting::LoggingUseCMTrace>() == false);
955+
REQUIRE(userSettingTest.GetWarnings().size() == 0);
956+
}
957+
SECTION("Bad value type")
958+
{
959+
std::string_view json = R"({ "logging": { "useCMTrace": "yes" } })";
960+
SetSetting(Stream::PrimaryUserSettings, json);
961+
UserSettingsTest userSettingTest;
962+
963+
REQUIRE(userSettingTest.Get<Setting::LoggingUseCMTrace>() == false);
964+
REQUIRE(userSettingTest.GetWarnings().size() == 1);
965+
}
966+
}
967+
928968
TEST_CASE("ConvertToSortField", "[settings]")
929969
{
930970
SECTION("Valid values - lowercase")

src/AppInstallerCommonCore/FileLogger.cpp

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,53 @@ namespace AppInstaller::Logging
2929
return std::move(strstr).str();
3030
}
3131

32+
// Formats a log line in CMTrace format.
33+
// CMTrace log format: <![LOG[message]LOG]!><time="HH:mm:ss.fff+###" date="MM-dd-YYYY" component="channel" context="" type="N" thread="TID" file="">
34+
std::string ToCMTraceLogLine(Channel channel, Level level, std::string_view message)
35+
{
36+
auto now = std::chrono::system_clock::now();
37+
auto tt = std::chrono::system_clock::to_time_t(now);
38+
tm localTime{};
39+
_localtime64_s(&localTime, &tt);
40+
41+
auto sinceEpoch = now.time_since_epoch();
42+
auto leftoverMillis = std::chrono::duration_cast<std::chrono::milliseconds>(sinceEpoch) - std::chrono::duration_cast<std::chrono::seconds>(sinceEpoch);
43+
44+
// Get UTC bias in minutes (positive means west of UTC, CMTrace uses positive for west)
45+
long timezoneBiasSeconds = 0;
46+
_get_timezone(&timezoneBiasSeconds);
47+
long biasMins = timezoneBiasSeconds / 60;
48+
49+
// CMTrace type: 1=Info/Verbose, 2=Warning, 3=Error/Critical
50+
int type;
51+
switch (level)
52+
{
53+
case Level::Warning: type = 2; break;
54+
case Level::Error:
55+
case Level::Crit: type = 3; break;
56+
default: type = 1; break;
57+
}
58+
59+
std::stringstream strstr;
60+
strstr << "<![LOG[" << message << "]LOG]!>"
61+
<< "<time=\""
62+
<< std::setw(2) << std::setfill('0') << localTime.tm_hour << ":"
63+
<< std::setw(2) << std::setfill('0') << localTime.tm_min << ":"
64+
<< std::setw(2) << std::setfill('0') << localTime.tm_sec << "."
65+
<< std::setw(3) << std::setfill('0') << leftoverMillis.count()
66+
<< "+" << biasMins << "\""
67+
<< " date=\""
68+
<< std::setw(2) << std::setfill('0') << (1 + localTime.tm_mon) << "-"
69+
<< std::setw(2) << std::setfill('0') << localTime.tm_mday << "-"
70+
<< (1900 + localTime.tm_year) << "\""
71+
<< " component=\"" << GetChannelName(channel) << "\""
72+
<< " context=\"\""
73+
<< " type=\"" << type << "\""
74+
<< " thread=\"" << GetCurrentThreadId() << "\""
75+
<< " file=\"\">";
76+
return std::move(strstr).str();
77+
}
78+
3279
// Determines the difference between the given position and the maximum as an offset.
3380
std::ofstream::off_type CalculateDiff(const std::ofstream::pos_type& position, std::ofstream::off_type maximum)
3481
{
@@ -94,7 +141,15 @@ namespace AppInstaller::Logging
94141

95142
void FileLogger::Write(Channel channel, Level level, std::string_view message) noexcept try
96143
{
97-
std::string log = ToLogLine(channel, level, message);
144+
std::string log;
145+
if (Settings::User().Get<Settings::Setting::LoggingUseCMTrace>())
146+
{
147+
log = ToCMTraceLogLine(channel, level, message);
148+
}
149+
else
150+
{
151+
log = ToLogLine(channel, level, message);
152+
}
98153
WriteDirect(channel, level, log);
99154
}
100155
catch (...) {}

src/AppInstallerCommonCore/Public/winget/UserSettings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ namespace AppInstaller::Settings
133133
LoggingFileTotalSizeLimitInMB,
134134
LoggingFileIndividualSizeLimitInMB,
135135
LoggingFileCountLimit,
136+
LoggingUseCMTrace,
136137
// Uninstall behavior
137138
UninstallPurgePortablePackage,
138139
// Download behavior
@@ -237,6 +238,7 @@ namespace AppInstaller::Settings
237238
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileTotalSizeLimitInMB, uint32_t, uint32_t, 128, ".logging.file.totalSizeLimitInMB"sv);
238239
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileIndividualSizeLimitInMB, uint32_t, uint32_t, 16, ".logging.file.individualSizeLimitInMB"sv);
239240
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileCountLimit, uint32_t, uint32_t, 0, ".logging.file.countLimit"sv);
241+
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingUseCMTrace, bool, bool, false, ".logging.useCMTrace"sv);
240242
// Interactivity
241243
SETTINGMAPPING_SPECIALIZATION(Setting::InteractivityDisable, bool, bool, false, ".interactivity.disable"sv);
242244
// Output behavior

src/AppInstallerCommonCore/UserSettings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ namespace AppInstaller::Settings
299299
WINGET_VALIDATE_PASS_THROUGH(LoggingFileTotalSizeLimitInMB)
300300
WINGET_VALIDATE_PASS_THROUGH(LoggingFileIndividualSizeLimitInMB)
301301
WINGET_VALIDATE_PASS_THROUGH(LoggingFileCountLimit)
302+
WINGET_VALIDATE_PASS_THROUGH(LoggingUseCMTrace)
302303

303304
#ifndef AICLI_DISABLE_TEST_HOOKS
304305
WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump)

0 commit comments

Comments
 (0)