Skip to content

Commit 37f432d

Browse files
committed
Add setting for installer log file names
1 parent 7d72eb3 commit 37f432d

File tree

7 files changed

+196
-39
lines changed

7 files changed

+196
-39
lines changed

doc/Settings.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,16 @@ In addition, there are special values that cover multiple channels. `default` i
286286
},
287287
```
288288

289+
### fileNameStrategy
290+
291+
Sets the default strategy for naming log files for installers that support it. `manifest` is the default and uses the manifest name. `timestamp` uses the date and time. `guid` uses a generated GUID. `shortguid` uses the first 8 characters of a generated GUID. Invalid values will revert to `manifest`.
292+
293+
```json
294+
"logging": {
295+
"fileNameStrategy": "manifest" | "timestamp" | "guid" | "shortguid"
296+
},
297+
```
298+
289299
## Network
290300

291301
The `network` settings influence how winget uses the network to retrieve packages and metadata.

schemas/JSON/settings/settings.schema.0.2.json

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -46,42 +46,52 @@
4646
"Logging": {
4747
"description": "Logging settings",
4848
"type": "object",
49-
"properties": {
50-
"level": {
51-
"description": "Preferred logging level",
52-
"type": "string",
53-
"enum": [
54-
"verbose",
55-
"info",
56-
"warning",
57-
"error",
58-
"critical"
59-
]
60-
},
61-
"channels": {
62-
"description": "The logging channels to enable",
63-
"type": "array",
64-
"items": {
65-
"uniqueItems": true,
66-
"type": "string",
67-
"enum": [
68-
"fail",
69-
"cli",
70-
"sql",
71-
"repo",
72-
"yaml",
73-
"core",
74-
"test",
75-
"config",
76-
"default",
77-
"all"
78-
],
79-
"maxLength": 20
80-
},
81-
"minItems": 0,
82-
"maxItems": 20
49+
"properties": {
50+
"level": {
51+
"description": "Preferred logging level",
52+
"type": "string",
53+
"enum": [
54+
"verbose",
55+
"info",
56+
"warning",
57+
"error",
58+
"critical"
59+
]
60+
},
61+
"channels": {
62+
"description": "The logging channels to enable",
63+
"type": "array",
64+
"items": {
65+
"uniqueItems": true,
66+
"type": "string",
67+
"enum": [
68+
"fail",
69+
"cli",
70+
"sql",
71+
"repo",
72+
"yaml",
73+
"core",
74+
"test",
75+
"config",
76+
"default",
77+
"all"
78+
],
79+
"maxLength": 20
80+
},
81+
"minItems": 0,
82+
"maxItems": 20
83+
},
84+
"fileNameStrategy": {
85+
"description": "Controls the default naming of log files for installers that support it",
86+
"type": "string",
87+
"enum": [
88+
"manifest",
89+
"timestamp",
90+
"guid",
91+
"shortguid"
92+
]
93+
}
8394
}
84-
}
8595
},
8696
"InstallPrefReq": {
8797
"description": "Shared schema for preferences and requirements",

src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,41 @@ namespace AppInstaller::CLI::Workflow
171171
else
172172
{
173173
const auto& manifest = context.Get<Execution::Data::Manifest>();
174-
174+
const Logging::LogNameStrategy logNameStrategy = Settings::User().Get<Settings::Setting::LoggingFileNameStrategy>();
175175
auto path = Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation);
176-
path /= Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version);
177-
path += '-';
178-
path += Utility::GetCurrentTimeForFilename(true);
176+
177+
switch (logNameStrategy)
178+
{
179+
case Logging::LogNameStrategy::Manifest:
180+
// Use manifest ID and version for log file name
181+
// Results in <DefaultLogLocation>\<ManifestId>.<ManifestVersion>-<Timestamp>.log
182+
path /= Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version);
183+
path += '-';
184+
path += Utility::GetCurrentTimeForFilename(true);
185+
break;
186+
case Logging::LogNameStrategy::Timestamp:
187+
// Use only timestamp for log file name
188+
// Results in <DefaultLogLocation>\<Timestamp>.log
189+
path /= Utility::GetCurrentTimeForFilename(true);
190+
break;
191+
case Logging::LogNameStrategy::Guid:
192+
// Use a GUID for log file name
193+
// Results in <DefaultLogLocation>\<GUID>.log
194+
path /= Utility::CreateNewGuidNameWString();
195+
break;
196+
case Logging::LogNameStrategy::ShortGuid:
197+
// Use the first 8 characters of a GUID for log file name
198+
// Results in <DefaultLogLocation>\<First 8 characters of GUID>.log
199+
path /= Utility::CreateNewGuidNameWString().substr(0, 8);
200+
break;
201+
default:
202+
// This should never happen due to validation when reading settings, but handle it just in case.
203+
AICLI_LOG(CLI, Error, << "Unknown log naming strategy.");
204+
THROW_HR(E_UNEXPECTED);
205+
break;
206+
}
207+
208+
// Add the extension to the log file regardless of naming strategy
179209
path += Logging::FileLogger::DefaultExt();
180210

181211
logPath = path.u8string();

src/AppInstallerCLITests/UserSettings.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,72 @@ TEST_CASE("SettingLoggingLevelPreference", "[settings]")
318318
REQUIRE(userSettingTest.Get<Setting::LoggingLevelPreference>() == Level::Info);
319319
REQUIRE(userSettingTest.GetWarnings().size() == 1);
320320
}
321+
}
322+
323+
TEST_CASE("SettingLoggingFileNameStrategy", "[settings]") {
324+
auto again = DeleteUserSettingsFiles();
325+
326+
SECTION("Default value")
327+
{
328+
UserSettingsTest userSettingTest;
329+
330+
REQUIRE(userSettingTest.Get<Setting::LoggingFileNameStrategy>() == LogNameStrategy::Manifest);
331+
REQUIRE(userSettingTest.GetWarnings().size() == 0);
332+
}
333+
SECTION("Manifest")
334+
{
335+
std::string_view json = R"({ "logging": { "fileNameStrategy": "manifest" } })";
336+
SetSetting(Stream::PrimaryUserSettings, json);
337+
UserSettingsTest userSettingTest;
338+
339+
REQUIRE(userSettingTest.Get<Setting::LoggingFileNameStrategy>() == LogNameStrategy::Manifest);
340+
REQUIRE(userSettingTest.GetWarnings().size() == 0);
341+
}
342+
SECTION("Timestamp")
343+
{
344+
std::string_view json = R"({ "logging": { "fileNameStrategy": "timestamp" } })";
345+
SetSetting(Stream::PrimaryUserSettings, json);
346+
UserSettingsTest userSettingTest;
347+
348+
REQUIRE(userSettingTest.Get<Setting::LoggingFileNameStrategy>() == LogNameStrategy::Timestamp);
349+
REQUIRE(userSettingTest.GetWarnings().size() == 0);
350+
}
351+
SECTION("Guid")
352+
{
353+
std::string_view json = R"({ "logging": { "fileNameStrategy": "guid" } })";
354+
SetSetting(Stream::PrimaryUserSettings, json);
355+
UserSettingsTest userSettingTest;
356+
357+
REQUIRE(userSettingTest.Get<Setting::LoggingFileNameStrategy>() == LogNameStrategy::Guid);
358+
REQUIRE(userSettingTest.GetWarnings().size() == 0);
359+
}
360+
SECTION("Short Guid")
361+
{
362+
std::string_view json = R"({ "logging": { "fileNameStrategy": "shortguid" } })";
363+
SetSetting(Stream::PrimaryUserSettings, json);
364+
UserSettingsTest userSettingTest;
365+
366+
REQUIRE(userSettingTest.Get<Setting::LoggingFileNameStrategy>() == LogNameStrategy::ShortGuid);
367+
REQUIRE(userSettingTest.GetWarnings().size() == 0);
368+
}
369+
SECTION("Bad value")
370+
{
371+
std::string_view json = R"({ "logging": { "fileNameStrategy": "fake" } })";
372+
SetSetting(Stream::PrimaryUserSettings, json);
373+
UserSettingsTest userSettingTest;
374+
375+
REQUIRE(userSettingTest.Get<Setting::LoggingFileNameStrategy>() == LogNameStrategy::Manifest);
376+
REQUIRE(userSettingTest.GetWarnings().size() == 1);
377+
}
378+
SECTION("Bad value type")
379+
{
380+
std::string_view json = R"({ "logging": { "fileNameStrategy": 5 } })";
381+
SetSetting(Stream::PrimaryUserSettings, json);
382+
UserSettingsTest userSettingTest;
383+
384+
REQUIRE(userSettingTest.Get<Setting::LoggingFileNameStrategy>() == LogNameStrategy::Manifest);
385+
REQUIRE(userSettingTest.GetWarnings().size() == 1);
386+
}
321387
}
322388

323389
TEST_CASE("SettingAutoUpdateIntervalInMinutes", "[settings]")

src/AppInstallerCommonCore/Public/winget/UserSettings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ namespace AppInstaller::Settings
101101
// Logging
102102
LoggingLevelPreference,
103103
LoggingChannelPreference,
104+
LoggingFileNameStrategy,
104105
// Uninstall behavior
105106
UninstallPurgePortablePackage,
106107
// Download behavior
@@ -196,6 +197,7 @@ namespace AppInstaller::Settings
196197
// Logging
197198
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingLevelPreference, std::string, Logging::Level, Logging::Level::Info, ".logging.level"sv);
198199
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingChannelPreference, std::vector<std::string>, Logging::Channel, Logging::Channel::Defaults, ".logging.channels"sv);
200+
SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileNameStrategy, std::string, Logging::LogNameStrategy, Logging::LogNameStrategy::Manifest, ".logging.fileNameStrategy"sv);
199201
// Interactivity
200202
SETTINGMAPPING_SPECIALIZATION(Setting::InteractivityDisable, bool, bool, false, ".interactivity.disable"sv);
201203

src/AppInstallerCommonCore/UserSettings.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,33 @@ namespace AppInstaller::Settings
473473

474474
return result;
475475
}
476+
477+
WINGET_VALIDATE_SIGNATURE(LoggingFileNameStrategy)
478+
{
479+
// logging name strategy possible values
480+
static constexpr std::string_view s_strategy_manifest = "manifest";
481+
static constexpr std::string_view s_strategy_timestamp = "timestamp";
482+
static constexpr std::string_view s_strategy_guid = "guid";
483+
static constexpr std::string_view s_strategy_shortguid = "shortguid";
484+
485+
if (Utility::CaseInsensitiveEquals(value, s_strategy_manifest))
486+
{
487+
return LogNameStrategy::Manifest;
488+
}
489+
else if (Utility::CaseInsensitiveEquals(value, s_strategy_timestamp))
490+
{
491+
return LogNameStrategy::Timestamp;
492+
}
493+
else if (Utility::CaseInsensitiveEquals(value, s_strategy_guid))
494+
{
495+
return LogNameStrategy::Guid;
496+
}
497+
else if (Utility::CaseInsensitiveEquals(value, s_strategy_shortguid))
498+
{
499+
return LogNameStrategy::ShortGuid;
500+
}
501+
return {};
502+
}
476503
}
477504

478505
#ifndef AICLI_DISABLE_TEST_HOOKS

src/AppInstallerSharedLib/Public/AppInstallerLogging.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ namespace AppInstaller::Logging
8383
Crit,
8484
};
8585

86+
enum class LogNameStrategy
87+
{
88+
// The log name is the name of the manifest with a timestamp
89+
Manifest,
90+
// The log name is just a timestamp
91+
Timestamp,
92+
// The log name is a GUID
93+
Guid,
94+
// The log name is the first 8 characters of a GUID
95+
ShortGuid,
96+
};
97+
8698
// The interface that a log target must implement.
8799
struct ILogger
88100
{

0 commit comments

Comments
 (0)