diff --git a/src/AppInstallerCLICore/COMContext.cpp b/src/AppInstallerCLICore/COMContext.cpp index 42f35b2b7d..216bc07bb3 100644 --- a/src/AppInstallerCLICore/COMContext.cpp +++ b/src/AppInstallerCLICore/COMContext.cpp @@ -44,18 +44,18 @@ namespace AppInstaller::CLI::Execution FireCallbacks(ReportType::EndProgress, 0, 0, ProgressType::None, m_executionStage); }; - void COMContext::SetExecutionStage(CLI::Workflow::ExecutionStage executionStage, bool) + void COMContext::SetExecutionStage(CLI::Workflow::ExecutionStage executionStage) { m_executionStage = executionStage; FireCallbacks(ReportType::ExecutionPhaseUpdate, 0, 0, ProgressType::None, m_executionStage); - Logging::SetExecutionStage(static_cast(m_executionStage)); + GetThreadGlobals().GetTelemetryLogger().SetExecutionStage(static_cast(m_executionStage)); } void COMContext::SetContextLoggers(const std::wstring_view telemetryCorrelationJson, const std::string& caller) { m_correlationData = telemetryCorrelationJson; - std::unique_ptr setThreadGlobalsToPreviousState = GetThreadGlobals().SetForCurrentThread(); + std::unique_ptr setThreadGlobalsToPreviousState = this->SetForCurrentThread(); SetLoggers(); GetThreadGlobals().GetTelemetryLogger().SetTelemetryCorrelationJson(telemetryCorrelationJson); diff --git a/src/AppInstallerCLICore/COMContext.h b/src/AppInstallerCLICore/COMContext.h index 75dbf53a62..0508e9981c 100644 --- a/src/AppInstallerCLICore/COMContext.h +++ b/src/AppInstallerCLICore/COMContext.h @@ -56,7 +56,7 @@ namespace AppInstaller::CLI::Execution void EndProgress(bool) override; //Execution::Context - void SetExecutionStage(CLI::Workflow::ExecutionStage executionPhase, bool); + void SetExecutionStage(CLI::Workflow::ExecutionStage executionPhase); CLI::Workflow::ExecutionStage GetExecutionStage() const { return m_executionStage; } diff --git a/src/AppInstallerCLICore/Commands/CompleteCommand.cpp b/src/AppInstallerCLICore/Commands/CompleteCommand.cpp index e762367c3e..7c6a22d7e3 100644 --- a/src/AppInstallerCLICore/Commands/CompleteCommand.cpp +++ b/src/AppInstallerCLICore/Commands/CompleteCommand.cpp @@ -53,8 +53,10 @@ namespace AppInstaller::CLI } // Create a new Context to execute the Complete from - auto subContextPtr = context.Clone(); + auto subContextPtr = context.CreateSubContext(); Context& subContext = *subContextPtr; + auto previousThreadGlobals = subContext.SetForCurrentThread(); + subContext.Reporter.SetChannel(Execution::Reporter::Channel::Completion); subContext.Add(std::move(data)); diff --git a/src/AppInstallerCLICore/ContextOrchestrator.cpp b/src/AppInstallerCLICore/ContextOrchestrator.cpp index ca88625997..2cc38a8b42 100644 --- a/src/AppInstallerCLICore/ContextOrchestrator.cpp +++ b/src/AppInstallerCLICore/ContextOrchestrator.cpp @@ -230,7 +230,7 @@ namespace AppInstaller::CLI::Execution { std::unique_ptr command = item->PopNextCommand(); - std::unique_ptr setThreadGlobalsToPreviousState = item->GetContext().GetThreadGlobals().SetForCurrentThread(); + std::unique_ptr setThreadGlobalsToPreviousState = item->GetContext().SetForCurrentThread(); item->GetContext().GetThreadGlobals().GetTelemetryLogger().LogCommand(command->FullName()); command->ValidateArguments(item->GetContext().Args); diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index 46efb7ede8..9baa0b3936 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -58,6 +58,12 @@ namespace AppInstaller::CLI } #endif + Logging::UseGlobalTelemetryLoggerActivityIdOnly(); + + Execution::Context context{ std::cout, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + context.EnableCtrlHandler(); + // 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); @@ -73,9 +79,6 @@ namespace AppInstaller::CLI // Initiate the background cleanup of the log file location. Logging::BeginLogFileCleanup(); - Execution::Context context{ std::cout, std::cin }; - context.EnableCtrlHandler(); - context << Workflow::ReportExecutionStage(Workflow::ExecutionStage::ParseArgs); // Convert incoming wide char args to UTF8 diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 6d03e5b6f3..0447b156ee 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -118,9 +118,9 @@ namespace AppInstaller::CLI::Execution } } - std::unique_ptr Context::Clone() + std::unique_ptr Context::CreateSubContext() { - auto clone = std::make_unique(Reporter); + auto clone = std::make_unique(Reporter, m_threadGlobals); clone->m_flags = m_flags; // If the parent is hooked up to the CTRL signal, have the clone be as well if (m_disableCtrlHandlerOnExit) @@ -184,6 +184,7 @@ namespace AppInstaller::CLI::Execution void Context::SetTerminationHR(HRESULT hr) { m_terminationHR = hr; + m_isTerminated = true; } void Context::Cancel(bool exitIfStuck, bool bypassUser) @@ -192,19 +193,19 @@ namespace AppInstaller::CLI::Execution Reporter.CancelInProgressTask(bypassUser); } - void Context::SetExecutionStage(Workflow::ExecutionStage stage, bool allowBackward) + void Context::SetExecutionStage(Workflow::ExecutionStage stage) { if (m_executionStage == stage) { return; } - else if (m_executionStage > stage && !allowBackward) + else if (m_executionStage > stage) { THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), "Reporting ExecutionStage to an earlier Stage without allowBackward as true"); } m_executionStage = stage; - Logging::SetExecutionStage(static_cast(m_executionStage)); + GetThreadGlobals().GetTelemetryLogger().SetExecutionStage(static_cast(m_executionStage)); } AppInstaller::ThreadLocalStorage::ThreadGlobals& Context::GetThreadGlobals() @@ -212,6 +213,11 @@ namespace AppInstaller::CLI::Execution return m_threadGlobals; } + std::unique_ptr Context::SetForCurrentThread() + { + return m_threadGlobals.SetForCurrentThread(); + } + #ifndef AICLI_DISABLE_TEST_HOOKS bool Context::ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task) { diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 24482799c7..d087c8836f 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -70,8 +70,10 @@ namespace AppInstaller::CLI::Execution { Context(std::ostream& out, std::istream& in) : Reporter(out, in) {} - // Clone the reporter for this constructor. - Context(Execution::Reporter& reporter) : Reporter(reporter, Execution::Reporter::clone_t{}) {} + // Constructor for creating a sub-context. + Context(Execution::Reporter& reporter, ThreadLocalStorage::ThreadGlobals& threadGlobals) : + Reporter(reporter, Execution::Reporter::clone_t{}), + m_threadGlobals(threadGlobals, ThreadLocalStorage::ThreadGlobals::create_sub_thread_globals_t{}) {} virtual ~Context(); @@ -81,8 +83,8 @@ namespace AppInstaller::CLI::Execution // The arguments given to execute with. Args Args; - // Creates a copy of this context as it was at construction. - virtual std::unique_ptr Clone(); + // Creates a child of this context. + virtual std::unique_ptr CreateSubContext(); // Enables reception of CTRL signals. void EnableCtrlHandler(bool enabled = true); @@ -125,11 +127,13 @@ namespace AppInstaller::CLI::Execution WI_ClearAllFlags(m_flags, flags); } - virtual void SetExecutionStage(Workflow::ExecutionStage stage, bool); + virtual void SetExecutionStage(Workflow::ExecutionStage stage); - // Get Globals for Current Thread + // Get Globals for Current Context AppInstaller::ThreadLocalStorage::ThreadGlobals& GetThreadGlobals(); + std::unique_ptr SetForCurrentThread(); + #ifndef AICLI_DISABLE_TEST_HOOKS // Enable tests to override behavior bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index 0c06bc9024..d37abc7b12 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -53,36 +53,7 @@ namespace AppInstaller::CLI::Execution Max }; - // Contains all the information needed to install a package. - // This is used when installing multiple packages to pass all the - // data to a sub-context. - struct PackageToInstall - { - PackageToInstall( - std::shared_ptr&& packageVersion, - std::shared_ptr&& installedPackageVersion, - Manifest::Manifest&& manifest, - Manifest::ManifestInstaller&& installer, - Manifest::ScopeEnum scope = Manifest::ScopeEnum::Unknown, - uint32_t packageSubExecutionId = 0) - : PackageVersion(std::move(packageVersion)), InstalledPackageVersion(std::move(installedPackageVersion)), Manifest(std::move(manifest)), Installer(std::move(installer)), Scope(scope), PackageSubExecutionId(packageSubExecutionId) { } - - std::shared_ptr PackageVersion; - - // Used to uninstall the old version if needed. - std::shared_ptr InstalledPackageVersion; - - // Use this instead of the PackageVersion->GetManifest() as the locale was - // applied when selecting the installer. - Manifest::Manifest Manifest; - - Manifest::ManifestInstaller Installer; - Manifest::ScopeEnum Scope = Manifest::ScopeEnum::Unknown; - - // Use this sub execution id when installing this package so that - // install telemetry is captured with the same sub execution id as other events in Search phase. - uint32_t PackageSubExecutionId = 0; - }; + struct Context; namespace details { @@ -203,7 +174,7 @@ namespace AppInstaller::CLI::Execution template <> struct DataMapping { - using value_t = std::vector; + using value_t = std::vector>; }; template <> diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 6a269be806..40efcaf708 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -13,6 +13,25 @@ using namespace AppInstaller::Manifest; namespace AppInstaller::CLI::Workflow { + namespace + { + // Contains all the information needed to install a dependency package. + struct DependencyPackageCandidate + { + DependencyPackageCandidate( + std::shared_ptr&& packageVersion, + std::shared_ptr&& installedPackageVersion, + Manifest::Manifest&& manifest, + Manifest::ManifestInstaller&& installer) + : PackageVersion(std::move(packageVersion)), InstalledPackageVersion(std::move(installedPackageVersion)), Manifest(std::move(manifest)), Installer(std::move(installer)) { } + + std::shared_ptr PackageVersion; + std::shared_ptr InstalledPackageVersion; + Manifest::Manifest Manifest; + Manifest::ManifestInstaller Installer; + }; + } + void ReportDependencies::operator()(Execution::Context& context) const { if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) @@ -20,7 +39,7 @@ namespace AppInstaller::CLI::Workflow return; } auto info = context.Reporter.Info(); - + const auto& dependencies = context.Get(); if (dependencies.HasAny()) { @@ -114,7 +133,6 @@ namespace AppInstaller::CLI::Workflow } } - void ManagePackageDependencies::operator()(Execution::Context& context) const { if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) @@ -127,7 +145,7 @@ namespace AppInstaller::CLI::Workflow const auto& rootManifest = context.Get(); Dependency rootAsDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); - + const auto& rootInstaller = context.Get(); const auto& rootDependencies = rootInstaller->Dependencies; @@ -146,25 +164,25 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); } - - std::map idToPackageMap; + std::map idToPackageMap; bool foundError = false; DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, - [&](Dependency node) { + [&](Dependency node) + { DependencyNodeProcessor nodeProcessor(context); - + auto result = nodeProcessor.EvaluateDependencies(node); DependencyList list = nodeProcessor.GetDependencyList(); foundError = foundError || (result == DependencyNodeProcessorResult::Error); if (result == DependencyNodeProcessorResult::Success) { - Execution::PackageToInstall packageToInstall{ + DependencyPackageCandidate dependencyPackageCandidate{ std::move(nodeProcessor.GetPackageLatestVersion()), std::move(nodeProcessor.GetPackageInstalledVersion()), std::move(nodeProcessor.GetManifest()), std::move(nodeProcessor.GetPreferredInstaller()) }; - idToPackageMap.emplace(node.Id, std::move(packageToInstall)); + idToPackageMap.emplace(node.Id, std::move(dependencyPackageCandidate)); }; return list; @@ -185,21 +203,43 @@ namespace AppInstaller::CLI::Workflow const auto& installationOrder = dependencyGraph.GetInstallationOrder(); - std::vector installers; + std::vector> dependencyPackageContexts; for (auto const& node : installationOrder) - { + { auto itr = idToPackageMap.find(node.Id); // if the package was already installed (with a useful version) or is the root // then there will be no installer for it on the map. if (itr != idToPackageMap.end()) { - installers.push_back(std::move(itr->second)); + auto dependencyContextPtr = context.CreateSubContext(); + Execution::Context& dependencyContext = *dependencyContextPtr; + auto previousThreadGlobals = dependencyContext.SetForCurrentThread(); + + Logging::Telemetry().LogSelectedInstaller( + static_cast(itr->second.Installer.Arch), + itr->second.Installer.Url, + Manifest::InstallerTypeToString(itr->second.Installer.InstallerType), + Manifest::ScopeToString(itr->second.Installer.Scope), + itr->second.Installer.Locale); + + Logging::Telemetry().LogManifestFields( + itr->second.Manifest.Id, + itr->second.Manifest.DefaultLocalization.Get(), + itr->second.Manifest.Version); + + // Extract the data needed for installing + dependencyContext.Add(itr->second.PackageVersion); + dependencyContext.Add(itr->second.Manifest); + dependencyContext.Add(itr->second.InstalledPackageVersion); + dependencyContext.Add(itr->second.Installer); + + dependencyPackageContexts.emplace_back(std::move(dependencyContextPtr)); } } - + // Install dependencies in the correct order - context.Add(installers); + context.Add(std::move(dependencyPackageContexts)); context << Workflow::InstallMultiplePackages(m_dependencyReportMessage, APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES, {}, false, true); } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp index 271e33cf13..6592c9efc3 100644 --- a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp +++ b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp @@ -9,8 +9,8 @@ using namespace AppInstaller::Repository; namespace AppInstaller::CLI::Workflow { - DependencyNodeProcessor::DependencyNodeProcessor(Execution::Context& context) - : m_context(context) {} + DependencyNodeProcessor::DependencyNodeProcessor(Execution::Context& context) + : m_context(context) {} DependencyNodeProcessorResult DependencyNodeProcessor::EvaluateDependencies(Dependency& dependencyNode) { diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.h b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.h index cfbebbeb54..099bce7472 100644 --- a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.h +++ b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.h @@ -11,35 +11,35 @@ using namespace AppInstaller::Repository; namespace AppInstaller::CLI::Workflow { - enum DependencyNodeProcessorResult - { - Error, - Success, - Skipped, - }; + enum DependencyNodeProcessorResult + { + Error, + Success, + Skipped, + }; - struct DependencyNodeProcessor - { - DependencyNodeProcessor(Execution::Context& context); + struct DependencyNodeProcessor + { + DependencyNodeProcessor(Execution::Context& context); - DependencyNodeProcessorResult EvaluateDependencies(Dependency& dependencyNode); + DependencyNodeProcessorResult EvaluateDependencies(Dependency& dependencyNode); - DependencyList GetDependencyList() { return m_dependenciesList; } + DependencyList GetDependencyList() { return m_dependenciesList; } - std::shared_ptr GetPackageLatestVersion() { return m_nodePackageLatestVersion; } + std::shared_ptr GetPackageLatestVersion() { return m_nodePackageLatestVersion; } - std::shared_ptr GetPackageInstalledVersion() { return m_nodePackageInstalledVersion; } + std::shared_ptr GetPackageInstalledVersion() { return m_nodePackageInstalledVersion; } - Manifest::Manifest GetManifest() { return m_nodeManifest; } + Manifest::Manifest GetManifest() { return m_nodeManifest; } - Manifest::ManifestInstaller GetPreferredInstaller() { return m_installer; } + Manifest::ManifestInstaller GetPreferredInstaller() { return m_installer; } - private: - Execution::Context& m_context; - DependencyList m_dependenciesList; - std::shared_ptr m_nodePackageLatestVersion; - std::shared_ptr m_nodePackageInstalledVersion; - Manifest::ManifestInstaller m_installer; - Manifest::Manifest m_nodeManifest; - }; + private: + Execution::Context& m_context; + DependencyList m_dependenciesList; + std::shared_ptr m_nodePackageLatestVersion; + std::shared_ptr m_nodePackageInstalledVersion; + Manifest::ManifestInstaller m_installer; + Manifest::Manifest m_nodeManifest; + }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index b3c58852bb..f734799e66 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -246,7 +246,7 @@ namespace AppInstaller::CLI::Workflow void SearchPackagesForImport(Execution::Context& context) { const auto& sources = context.Get(); - std::vector packagesToInstall = {}; + std::vector> packagesToInstall; bool foundAll = true; // Look for the packages needed from each source independently. @@ -266,15 +266,16 @@ namespace AppInstaller::CLI::Workflow AICLI_LOG(CLI, Info, << "Searching for packages requested from source [" << requiredSource.Details.Identifier << "]"); for (const auto& packageRequest : requiredSource.Packages) { - Logging::SubExecutionTelemetryScope subExecution; AICLI_LOG(CLI, Info, << "Searching for package [" << packageRequest.Id << "]"); // Search for the current package SearchRequest searchRequest; searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, packageRequest.Id.get())); - auto searchContextPtr = context.Clone(); + auto searchContextPtr = context.CreateSubContext(); Execution::Context& searchContext = *searchContextPtr; + auto previousThreadGlobals = searchContext.SetForCurrentThread(); + searchContext.Add(source); searchContext.Add(source.Search(searchRequest)); @@ -320,13 +321,7 @@ namespace AppInstaller::CLI::Workflow } } - packagesToInstall.emplace_back( - std::move(searchContext.Get()), - std::move(searchContext.Get()), - std::move(searchContext.Get()), - std::move(searchContext.Get().value()), - packageRequest.Scope, - subExecution.GetCurrentSubExecutionId()); + packagesToInstall.emplace_back(std::move(searchContextPtr)); } } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 9424dabeaa..7f3712e4e1 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -189,13 +189,11 @@ namespace AppInstaller::CLI::Workflow void EnsurePackageAgreementsAcceptanceForMultipleInstallers(Execution::Context& context) { bool hasPackageAgreements = false; - for (auto package : context.Get()) + for (auto& packageContext : context.Get()) { - // Show agreements for each package in a sub-context - auto showContextPtr = context.Clone(); - Execution::Context& showContext = *showContextPtr; - - showContext.Add(package.Manifest); + // Show agreements for each package + Execution::Context& showContext = *packageContext; + auto previousThreadGlobals = showContext.SetForCurrentThread(); showContext << Workflow::ReportManifestIdentityWithVersion << @@ -205,7 +203,7 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(showContext.GetTerminationHR()); } - hasPackageAgreements |= !package.Manifest.CurrentLocalization.Get().empty(); + hasPackageAgreements |= !showContext.Get().CurrentLocalization.Get().empty(); } // If any package has agreements, ensure they are accepted @@ -411,9 +409,9 @@ namespace AppInstaller::CLI::Workflow if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { DependencyList allDependencies; - for (auto package : context.Get()) + for (auto& packageContext : context.Get()) { - allDependencies.Add(package.Installer.Dependencies); + allDependencies.Add(packageContext->Get().value().Dependencies); } context.Add(allDependencies); @@ -424,22 +422,14 @@ namespace AppInstaller::CLI::Workflow size_t packagesCount = context.Get().size(); size_t packagesProgress = 0; - for (auto package : context.Get()) + for (auto& packageContext : context.Get()) { - Logging::SubExecutionTelemetryScope subExecution{ package.PackageSubExecutionId }; - packagesProgress++; context.Reporter.Info() << "(" << packagesProgress << "/" << packagesCount << ") "; // We want to do best effort to install all packages regardless of previous failures - auto installContextPtr = context.Clone(); - Execution::Context& installContext = *installContextPtr; - - // Extract the data needed for installing - installContext.Add(package.PackageVersion); - installContext.Add(package.Manifest); - installContext.Add(package.InstalledPackageVersion); - installContext.Add(package.Installer); + Execution::Context& installContext = *packageContext; + auto previousThreadGlobals = installContext.SetForCurrentThread(); installContext << Workflow::ReportIdentityAndInstallationDisclaimer; if (!m_ignorePackageDependencies) diff --git a/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp b/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp index 213599b9f8..162edb9e47 100644 --- a/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp +++ b/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp @@ -562,13 +562,6 @@ namespace AppInstaller::CLI::Workflow return { {}, std::move(inapplicabilitiesInstallers) }; } - Logging::Telemetry().LogSelectedInstaller( - static_cast(result->Arch), - result->Url, - Manifest::InstallerTypeToString(result->InstallerType), - Manifest::ScopeToString(result->Scope), - result->Locale); - return { *result, std::move(inapplicabilitiesInstallers) }; } diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp index f1b18fe5da..a64924d1e3 100644 --- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -19,19 +19,19 @@ namespace AppInstaller::CLI::Workflow return (installedVersion < updateVersion || updateVersion.IsLatest()); } - void AddToPackagesToInstallIfNotPresent(std::vector& packagesToInstall, Execution::PackageToInstall&& package) + void AddToPackagesToInstallIfNotPresent(std::vector>& packagesToInstall, std::unique_ptr packageContext) { for (auto const& existing : packagesToInstall) { - if (existing.Manifest.Id == package.Manifest.Id && - existing.Manifest.Version == package.Manifest.Version && - existing.PackageVersion->GetProperty(PackageVersionProperty::SourceIdentifier) == package.PackageVersion->GetProperty(PackageVersionProperty::SourceIdentifier)) + if (existing->Get().Id == packageContext->Get().Id && + existing->Get().Version == packageContext->Get().Version && + existing->Get()->GetProperty(PackageVersionProperty::SourceIdentifier) == packageContext->Get()->GetProperty(PackageVersionProperty::SourceIdentifier)) { return; } } - packagesToInstall.emplace_back(std::move(package)); + packagesToInstall.emplace_back(std::move(packageContext)); } } @@ -68,6 +68,18 @@ namespace AppInstaller::CLI::Workflow continue; } + Logging::Telemetry().LogSelectedInstaller( + static_cast(installer->Arch), + installer->Url, + Manifest::InstallerTypeToString(installer->InstallerType), + Manifest::ScopeToString(installer->Scope), + installer->Locale); + + Logging::Telemetry().LogManifestFields( + manifest.Id, + manifest.DefaultLocalization.Get(), + manifest.Version); + // Since we already did installer selection, just populate the context Data manifest.ApplyLocale(installer->Locale); context.Add(std::move(manifest)); @@ -118,16 +130,15 @@ namespace AppInstaller::CLI::Workflow void UpdateAllApplicable(Execution::Context& context) { const auto& matches = context.Get().Matches; - std::vector packagesToInstall; + std::vector> packagesToInstall; bool updateAllFoundUpdate = false; for (const auto& match : matches) { - Logging::SubExecutionTelemetryScope subExecution; - // We want to do best effort to update all applicable updates regardless on previous update failure - auto updateContextPtr = context.Clone(); + auto updateContextPtr = context.CreateSubContext(); Execution::Context& updateContext = *updateContextPtr; + auto previousThreadGlobals = updateContext.SetForCurrentThread(); updateContext.Add(match.Package); @@ -143,14 +154,7 @@ namespace AppInstaller::CLI::Workflow updateAllFoundUpdate = true; - Execution::PackageToInstall package{ - std::move(updateContext.Get()), - std::move(updateContext.Get()), - std::move(updateContext.Get()), - std::move(updateContext.Get().value()) }; - package.PackageSubExecutionId = subExecution.GetCurrentSubExecutionId(); - - AddToPackagesToInstallIfNotPresent(packagesToInstall, std::move(package)); + AddToPackagesToInstallIfNotPresent(packagesToInstall, std::move(updateContextPtr)); } if (!updateAllFoundUpdate) diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index fe6d602d4e..f4260b7cfe 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -247,7 +247,7 @@ namespace AppInstaller::CLI::Workflow catch (const wil::ResultException& re) { // Even though they are logged at their source, log again here for completeness. - Logging::Telemetry().LogException("wil::ResultException", re.what()); + Logging::Telemetry().LogException(Logging::FailureTypeEnum::ResultException, re.what()); context.Reporter.Error() << Resource::String::UnexpectedErrorExecutingCommand << ' ' << std::endl << GetUserPresentableMessage(re) << std::endl; @@ -256,7 +256,7 @@ namespace AppInstaller::CLI::Workflow catch (const winrt::hresult_error& hre) { std::string message = GetUserPresentableMessage(hre); - Logging::Telemetry().LogException("winrt::hresult_error", message); + Logging::Telemetry().LogException(Logging::FailureTypeEnum::WinrtHResultError, message); context.Reporter.Error() << Resource::String::UnexpectedErrorExecutingCommand << ' ' << std::endl << message << std::endl; @@ -270,13 +270,13 @@ namespace AppInstaller::CLI::Workflow } catch (const Resource::ResourceOpenException& e) { - Logging::Telemetry().LogException("ResourceOpenException", e.what()); + Logging::Telemetry().LogException(Logging::FailureTypeEnum::ResourceOpen, e.what()); context.Reporter.Error() << GetUserPresentableMessage(e) << std::endl; return APPINSTALLER_CLI_ERROR_MISSING_RESOURCE_FILE; } catch (const std::exception& e) { - Logging::Telemetry().LogException("std::exception", e.what()); + Logging::Telemetry().LogException(Logging::FailureTypeEnum::StdException, e.what()); context.Reporter.Error() << Resource::String::UnexpectedErrorExecutingCommand << ' ' << std::endl << GetUserPresentableMessage(e) << std::endl; @@ -285,7 +285,7 @@ namespace AppInstaller::CLI::Workflow catch (...) { LOG_CAUGHT_EXCEPTION(); - Logging::Telemetry().LogException("unknown", {}); + Logging::Telemetry().LogException(Logging::FailureTypeEnum::Unknown, {}); context.Reporter.Error() << Resource::String::UnexpectedErrorExecutingCommand << " ???"_liv << std::endl; return APPINSTALLER_CLI_ERROR_COMMAND_FAILED; @@ -545,7 +545,6 @@ namespace AppInstaller::CLI::Workflow void ReportSearchResult(Execution::Context& context) { auto& searchResult = context.Get(); - Logging::Telemetry().LogSearchResultCount(searchResult.Matches.size()); bool sourceIsComposite = context.Get().IsComposite(); Execution::TableOutput<5> table(context.Reporter, @@ -621,7 +620,7 @@ namespace AppInstaller::CLI::Workflow } } - AICLI_TERMINATE_CONTEXT(overallHR); + context.SetTerminationHR(overallHR); } } } @@ -774,6 +773,8 @@ namespace AppInstaller::CLI::Workflow { auto& searchResult = context.Get(); + Logging::Telemetry().LogSearchResultCount(searchResult.Matches.size()); + if (searchResult.Matches.size() == 0) { Logging::Telemetry().LogNoAppMatch(); @@ -983,6 +984,16 @@ namespace AppInstaller::CLI::Workflow } } + if (installer.has_value()) + { + Logging::Telemetry().LogSelectedInstaller( + static_cast(installer->Arch), + installer->Url, + Manifest::InstallerTypeToString(installer->InstallerType), + Manifest::ScopeToString(installer->Scope), + installer->Locale); + } + context.Add(installer); } @@ -1052,7 +1063,7 @@ namespace AppInstaller::CLI::Workflow void ReportExecutionStage::operator()(Execution::Context& context) const { - context.SetExecutionStage(m_stage, m_allowBackward); + context.SetExecutionStage(m_stage); } void HandleSourceAgreements::operator()(Execution::Context& context) const diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index 81ec9a067b..ab526554db 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -352,13 +352,12 @@ namespace AppInstaller::CLI::Workflow // Outputs: ExecutionStage struct ReportExecutionStage : public WorkflowTask { - ReportExecutionStage(ExecutionStage stage, bool allowBackward = false) : WorkflowTask("ReportExecutionStage"), m_stage(stage), m_allowBackward(allowBackward) {} + ReportExecutionStage(ExecutionStage stage) : WorkflowTask("ReportExecutionStage"), m_stage(stage) {} void operator()(Execution::Context& context) const override; private: ExecutionStage m_stage; - bool m_allowBackward; }; // Handles all opened source(s) agreements if needed. diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index a42c1fab2d..5de3d30afa 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -391,7 +391,7 @@ namespace m_overrides->emplace_back(wto); } - std::unique_ptr Clone() override + std::unique_ptr CreateSubContext() override { auto clone = std::make_unique(m_out, m_in, true, m_overrides); clone->SetFlags(this->GetFlags()); @@ -672,6 +672,7 @@ TEST_CASE("ExeInstallFlowWithTestManifest", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); @@ -695,6 +696,7 @@ TEST_CASE("InstallFlowNonZeroExitCode", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_NonZeroExitCode.yaml").GetPath().u8string()); @@ -719,6 +721,7 @@ TEST_CASE("InstallFlow_ExpectedReturnCodes", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_ExpectedReturnCodes.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Override, "/ExitCode 8"sv); @@ -739,6 +742,7 @@ TEST_CASE("InstallFlowWithNonApplicableArchitecture", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_NoApplicableArchitecture.yaml").GetPath().u8string()); InstallCommand install({}); @@ -757,6 +761,7 @@ TEST_CASE("MSStoreInstallFlowWithTestManifest", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForMSStore(context, false); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_MSStore.yaml").GetPath().u8string()); @@ -779,6 +784,7 @@ TEST_CASE("MsixInstallFlow_DownloadFlow", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForMSIX(context); OverrideForUpdateInstallerMotw(context); // Todo: point to files from our repo when the repo goes public @@ -804,6 +810,7 @@ TEST_CASE("MsixInstallFlow_StreamingFlow", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForMSIX(context); OverrideForCheckExistingInstaller(context); // Todo: point to files from our repo when the repo goes public @@ -832,6 +839,7 @@ TEST_CASE("MsiInstallFlow_DirectMsi", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForDirectMsi(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallerArgTest_Msi_NoSwitches.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Silent); @@ -854,6 +862,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); // Default Msi type with no args passed in, no switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Msi_NoSwitches.yaml")); context.Add(manifest); @@ -870,6 +879,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); // Msi type with /silent and /log and /custom and /installlocation, no switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Msi_NoSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); @@ -887,6 +897,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); // Msi type with /silent and /log and /custom and /installlocation, switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Msi_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); @@ -905,6 +916,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); // Default Inno type with no args passed in, no switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_NoSwitches.yaml")); context.Add(manifest); @@ -921,6 +933,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); // Inno type with /silent and /log and /custom and /installlocation, no switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_NoSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); @@ -938,6 +951,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); // Inno type with /silent and /log and /custom and /installlocation, switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); @@ -956,6 +970,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); // Override switch specified. The whole arg passed to installer is overridden. auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); @@ -976,6 +991,7 @@ TEST_CASE("InstallFlow_SearchAndInstall", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "TestQueryReturnOne"sv); @@ -998,6 +1014,7 @@ TEST_CASE("InstallFlow_SearchFoundNoApp", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context); context.Args.AddArg(Execution::Args::Type::Query, "TestQueryReturnZero"sv); @@ -1013,6 +1030,7 @@ TEST_CASE("InstallFlow_SearchFoundMultipleApp", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context); context.Args.AddArg(Execution::Args::Type::Query, "TestQueryReturnTwo"sv); @@ -1030,6 +1048,7 @@ TEST_CASE("InstallFlow_LicenseAgreement", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_LicenseAgreement.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); @@ -1057,6 +1076,7 @@ TEST_CASE("InstallFlow_LicenseAgreement_Prompt", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, installInput }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_LicenseAgreement.yaml").GetPath().u8string()); @@ -1086,6 +1106,7 @@ TEST_CASE("InstallFlow_LicenseAgreement_NotAccepted", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, installInput }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_LicenseAgreement.yaml").GetPath().u8string()); InstallCommand install({}); @@ -1108,6 +1129,7 @@ TEST_CASE("ShowFlow_SearchAndShowAppInfo", "[ShowFlow][workflow]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context); context.Args.AddArg(Execution::Args::Type::Query, "TestQueryReturnOne"sv); @@ -1126,6 +1148,7 @@ TEST_CASE("ShowFlow_SearchAndShowAppVersion", "[ShowFlow][workflow]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context); context.Args.AddArg(Execution::Args::Type::Query, "TestQueryReturnOne"sv); context.Args.AddArg(Execution::Args::Type::ListVersions); @@ -1144,6 +1167,7 @@ TEST_CASE("ShowFlow_Dependencies", "[ShowFlow][workflow][dependencies]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); TestUserSettings settings; @@ -1170,6 +1194,7 @@ TEST_CASE("DependencyGraph_SkipInstalled", "[InstallFlow][workflow][dependencyGr std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); Manifest manifest = CreateFakeManifestWithDependencies("DependenciesInstalled"); OverrideOpenDependencySource(context); @@ -1184,9 +1209,9 @@ TEST_CASE("DependencyGraph_SkipInstalled", "[InstallFlow][workflow][dependencyGr context << ManagePackageDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies); - std::vector installers = context.Get(); + auto& dependencyPackages = context.Get(); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); - REQUIRE(installers.size() == 0); + REQUIRE(dependencyPackages.size() == 0); } TEST_CASE("DependencyGraph_validMinVersions", "[InstallFlow][workflow][dependencyGraph][dependencies]") @@ -1195,6 +1220,7 @@ TEST_CASE("DependencyGraph_validMinVersions", "[InstallFlow][workflow][dependenc std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); Manifest manifest = CreateFakeManifestWithDependencies("DependenciesValidMinVersions"); OverrideOpenDependencySource(context); OverrideForInstallMultiplePackages(context); @@ -1208,13 +1234,13 @@ TEST_CASE("DependencyGraph_validMinVersions", "[InstallFlow][workflow][dependenc context << ManagePackageDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies); - std::vector installers = context.Get(); + auto& dependencyPackages = context.Get(); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); - REQUIRE(installers.size() == 1); - REQUIRE(installers.at(0).Manifest.Id == "minVersion"); + REQUIRE(dependencyPackages.size() == 1); + REQUIRE(dependencyPackages.at(0)->Get().Id == "minVersion"); // minVersion 1.5 is available but this requires 1.0 so that version is installed - REQUIRE(installers.at(0).Manifest.Version == "1.0"); + REQUIRE(dependencyPackages.at(0)->Get().Version == "1.0"); } TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]", ) @@ -1223,6 +1249,7 @@ TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); Manifest manifest = CreateFakeManifestWithDependencies("PathBetweenBranchesButNoLoop"); OverrideOpenDependencySource(context); OverrideForInstallMultiplePackages(context); @@ -1236,16 +1263,16 @@ TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph context << ManagePackageDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies); - std::vector installers = context.Get(); + auto& dependencyPackages = context.Get(); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); // Verify installers are called in order - REQUIRE(installers.size() == 4); - REQUIRE(installers.at(0).Manifest.Id == "B"); - REQUIRE(installers.at(1).Manifest.Id == "C"); - REQUIRE(installers.at(2).Manifest.Id == "G"); - REQUIRE(installers.at(3).Manifest.Id == "H"); + REQUIRE(dependencyPackages.size() == 4); + REQUIRE(dependencyPackages.at(0)->Get().Id == "B"); + REQUIRE(dependencyPackages.at(1)->Get().Id == "C"); + REQUIRE(dependencyPackages.at(2)->Get().Id == "G"); + REQUIRE(dependencyPackages.at(3)->Get().Id == "H"); } TEST_CASE("UpdateFlow_UpdateWithManifest", "[UpdateFlow][workflow]") @@ -1254,6 +1281,7 @@ TEST_CASE("UpdateFlow_UpdateWithManifest", "[UpdateFlow][workflow]") std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("UpdateFlowTest_Exe.yaml").GetPath().u8string()); @@ -1278,6 +1306,7 @@ TEST_CASE("UpdateFlow_UpdateWithManifestMSStore", "[UpdateFlow][workflow]") std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForMSStore(context, true); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_MSStore.yaml").GetPath().u8string()); @@ -1301,6 +1330,7 @@ TEST_CASE("UpdateFlow_UpdateWithManifestAppNotInstalled", "[UpdateFlow][workflow std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallerArgTest_Inno_NoSwitches.yaml").GetPath().u8string()); @@ -1320,6 +1350,7 @@ TEST_CASE("UpdateFlow_UpdateWithManifestVersionAlreadyInstalled", "[UpdateFlow][ std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); @@ -1339,6 +1370,7 @@ TEST_CASE("UpdateFlow_UpdateExe", "[UpdateFlow][workflow]") std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller"sv); @@ -1365,6 +1397,7 @@ TEST_CASE("UpdateFlow_UpdateMsix", "[UpdateFlow][workflow]") std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForMSIX(context); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestMsixInstaller"sv); @@ -1383,6 +1416,7 @@ TEST_CASE("UpdateFlow_UpdateMSStore", "[UpdateFlow][workflow]") std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForMSStore(context, true); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestMSStoreInstaller"sv); @@ -1406,6 +1440,7 @@ TEST_CASE("UpdateFlow_UpdateExeLatestAlreadyInstalled", "[UpdateFlow][workflow]" std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::Query, "TestExeInstallerWithLatestInstalled"sv); @@ -1425,6 +1460,7 @@ TEST_CASE("UpdateFlow_UpdateExeInstallerTypeNotApplicable", "[UpdateFlow][workfl std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::Query, "TestExeInstallerWithIncompatibleInstallerType"sv); @@ -1444,6 +1480,7 @@ TEST_CASE("UpdateFlow_UpdateExeInstallerTypeNotApplicableSpecificVersion", "[Upd std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::Query, "TestExeInstallerWithIncompatibleInstallerType"sv); context.Args.AddArg(Execution::Args::Type::Version, "2.0.0.0"sv); @@ -1464,6 +1501,7 @@ TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotFound", "[UpdateFlow][workflow] std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller"sv); context.Args.AddArg(Execution::Args::Type::Version, "1.2.3.4"sv); @@ -1484,6 +1522,7 @@ TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotApplicable", "[UpdateFlow][work std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::Query, "TestExeInstallerWithIncompatibleInstallerType"sv); context.Args.AddArg(Execution::Args::Type::Version, "1.0.0.0"sv); @@ -1506,6 +1545,7 @@ TEST_CASE("UpdateFlow_UpdateAllApplicable", "[UpdateFlow][workflow]") std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForShellExecute(context); OverrideForMSIX(context); @@ -1528,6 +1568,7 @@ TEST_CASE("UpdateFlow_UpgradeWithDuplicateUpgradeItemsFound", "[UpdateFlow][work std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); // Installer should only be run once since the 2 upgrade items are same. OverrideForShellExecute(context, 1); @@ -1548,6 +1589,7 @@ TEST_CASE("UpdateFlow_Dependencies", "[UpdateFlow][workflow][dependencies]") std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller.Dependencies"sv); @@ -1573,6 +1615,7 @@ TEST_CASE("UpdateFlow_LicenseAgreement", "[UpdateFlow][workflow]") std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "TestInstallerWithLicenseAgreement"sv); @@ -1599,6 +1642,7 @@ TEST_CASE("UpdateFlow_LicenseAgreement_NotAccepted", "[UpdateFlow][workflow]") std::ostringstream updateOutput; TestContext context{ updateOutput, updateInput }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::Query, "TestInstallerWithLicenseAgreement"sv); @@ -1624,6 +1668,7 @@ TEST_CASE("UpdateFlow_All_LicenseAgreement", "[UpdateFlow][workflow]") std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, /* upgradeUsesLicenses */ true); OverrideForShellExecute(context); OverrideForMSIX(context); @@ -1658,6 +1703,7 @@ TEST_CASE("UpdateFlow_All_LicenseAgreement_NotAccepted", "[UpdateFlow][workflow] std::ostringstream updateOutput; TestContext context{ updateOutput, updateInput }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, /* upgradeUsesLicenses */ true); context.Args.AddArg(Execution::Args::Type::All); @@ -1684,6 +1730,7 @@ TEST_CASE("UninstallFlow_UninstallExe", "[UninstallFlow][workflow]") std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForExeUninstall(context); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller"sv); @@ -1709,6 +1756,7 @@ TEST_CASE("UninstallFlow_UninstallMsix", "[UninstallFlow][workflow]") std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForMSIXUninstall(context); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestMsixInstaller"sv); @@ -1732,6 +1780,7 @@ TEST_CASE("UninstallFlow_UninstallMSStore", "[UninstallFlow][workflow]") std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); OverrideForMSIXUninstall(context); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestMSStoreInstaller"sv); @@ -1755,6 +1804,7 @@ TEST_CASE("UninstallFlow_UninstallExeNotFound", "[UninstallFlow][workflow]") std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.MissingApp"sv); context.Args.AddArg(Execution::Args::Type::Silent); @@ -1775,6 +1825,7 @@ TEST_CASE("ExportFlow_ExportAll", "[ExportFlow][workflow]") std::ostringstream exportOutput; TestContext context{ exportOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::OutputFile, exportResultPath); @@ -1809,6 +1860,7 @@ TEST_CASE("ExportFlow_ExportAll_WithVersions", "[ExportFlow][workflow]") std::ostringstream exportOutput; TestContext context{ exportOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context); context.Args.AddArg(Execution::Args::Type::OutputFile, exportResultPath); context.Args.AddArg(Execution::Args::Type::IncludeVersions); @@ -1845,6 +1897,7 @@ TEST_CASE("ImportFlow_Successful", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForMSIX(context); OverrideForShellExecute(context); @@ -1865,6 +1918,7 @@ TEST_CASE("ImportFlow_PackageAlreadyInstalled", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context, true); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-AlreadyInstalled.json").GetPath().string()); @@ -1883,6 +1937,7 @@ TEST_CASE("ImportFlow_IgnoreVersions", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-AlreadyInstalled.json").GetPath().string()); @@ -1902,6 +1957,7 @@ TEST_CASE("ImportFlow_MissingSource", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-UnknownSource.json").GetPath().string()); ImportCommand importCommand({}); @@ -1920,6 +1976,7 @@ TEST_CASE("ImportFlow_MissingPackage", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-UnknownPackage.json").GetPath().string()); @@ -1939,6 +1996,7 @@ TEST_CASE("ImportFlow_IgnoreMissingPackage", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-UnknownPackage.json").GetPath().string()); @@ -1959,6 +2017,7 @@ TEST_CASE("ImportFlow_MissingVersion", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-UnknownPackageVersion.json").GetPath().string()); @@ -1976,6 +2035,7 @@ TEST_CASE("ImportFlow_MalformedJsonFile", "[ImportFlow][workflow]") { std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-Malformed.json").GetPath().string()); ImportCommand importCommand({}); @@ -1990,6 +2050,7 @@ TEST_CASE("ImportFlow_InvalidJsonFile", "[ImportFlow][workflow]") { std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-Invalid.json").GetPath().string()); ImportCommand importCommand({}); @@ -2006,6 +2067,7 @@ TEST_CASE("ImportFlow_MachineScope", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-MachineScope.json").GetPath().string()); @@ -2030,6 +2092,7 @@ TEST_CASE("ImportFlow_Dependencies", "[ImportFlow][workflow][dependencies]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForMSIX(context); OverrideForShellExecute(context); @@ -2055,6 +2118,7 @@ TEST_CASE("ImportFlow_LicenseAgreement", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-WithLicenseAgreement.json").GetPath().string()); @@ -2079,6 +2143,7 @@ TEST_CASE("ImportFlow_LicenseAgreement_NotAccepted", "[ImportFlow][workflow]") std::ostringstream importOutput; TestContext context{ importOutput, importInput }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-WithLicenseAgreement.json").GetPath().string()); @@ -2119,6 +2184,7 @@ TEST_CASE("VerifyInstallerTrustLevelAndUpdateInstallerFileMotw", "[DownloadInsta std::ostringstream updateMotwOutput; TestContext context{ updateMotwOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Add({ {}, {} }); context.Add(testInstallerPath); auto packageVersion = std::make_shared(Manifest{}); @@ -2149,6 +2215,7 @@ TEST_CASE("InstallFlowMultiLocale_RequirementNotSatisfied", "[InstallFlow][workf std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); @@ -2168,6 +2235,7 @@ TEST_CASE("InstallFlowMultiLocale_RequirementSatisfied", "[InstallFlow][workflow std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Locale, "fr-FR"sv); @@ -2191,6 +2259,7 @@ TEST_CASE("InstallFlowMultiLocale_PreferenceNoBetterLocale", "[InstallFlow][work std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); @@ -2216,6 +2285,7 @@ TEST_CASE("InstallFlowMultiLocale_PreferenceWithBetterLocale", "[InstallFlow][wo std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); @@ -2244,6 +2314,7 @@ TEST_CASE("SourceAddFlow_Agreement", "[SourceAddFlow][workflow]") { std::ostringstream sourceAddOutput; TestContext context{ sourceAddOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForSourceAddWithAgreements(context); context.Args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Test"sv); @@ -2270,6 +2341,7 @@ TEST_CASE("SourceAddFlow_Agreement_Prompt_Yes", "[SourceAddFlow][workflow]") std::istringstream sourceAddInput{ "y" }; std::ostringstream sourceAddOutput; TestContext context{ sourceAddOutput, sourceAddInput }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForSourceAddWithAgreements(context); context.Args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Test"sv); @@ -2295,6 +2367,7 @@ TEST_CASE("SourceAddFlow_Agreement_Prompt_No", "[SourceAddFlow][workflow]") std::istringstream sourceAddInput{ "n" }; std::ostringstream sourceAddOutput; TestContext context{ sourceAddOutput, sourceAddInput }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForSourceAddWithAgreements(context, false); context.Args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Test"sv); @@ -2318,6 +2391,7 @@ TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") { std::ostringstream validateOutput; TestContext context{ validateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ValidateManifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); TestUserSettings settings; @@ -2345,6 +2419,7 @@ TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyG std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context, installationOrder); @@ -2372,6 +2447,7 @@ TEST_CASE("InstallerWithoutDependencies_RootDependenciesAreUsed", "[dependencies std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideDependencySource(context); @@ -2395,6 +2471,7 @@ TEST_CASE("DependenciesMultideclaration_InstallerDependenciesPreference", "[depe std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideDependencySource(context); @@ -2420,6 +2497,7 @@ TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideDependencySource(context); @@ -2464,6 +2542,7 @@ TEST_CASE("OpenSource_WithCustomHeader", "[OpenSource][CustomHeader]") std::ostringstream output; TestContext context{ output, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Query, "TestQuery"sv); context.Args.AddArg(Execution::Args::Type::CustomHeader, customHeader); context.Args.AddArg(Execution::Args::Type::Source, details.Name); @@ -2476,39 +2555,51 @@ TEST_CASE("AdminSetting_LocalManifestFiles", "[LocalManifests][workflow]") { RemoveSetting(Stream::AdminSettings); - // If there's no admin setting, using local manifest should fail. - Execution::Args args; - args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); - InstallCommand installCommand({}); - REQUIRE_THROWS(installCommand.ValidateArguments(args)); - - // Using settings command to enable local manifests - std::ostringstream settingsOutput; - TestContext context{ settingsOutput, std::cin }; - context.Args.AddArg(Execution::Args::Type::AdminSettingEnable, "LocalManifestFiles"sv); - context.Override({ EnsureRunningAsAdmin, [](TestContext&){} }); - SettingsCommand settings({}); - settings.Execute(context); - INFO(settingsOutput.str()); - - // Now using local manifests should succeed - Execution::Args args2; - args2.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); - InstallCommand installCommand2({}); - REQUIRE_NOTHROW(installCommand2.ValidateArguments(args2)); - - // Using settings command to disable local manifests - std::ostringstream settingsOutput2; - TestContext context2{ settingsOutput2, std::cin }; - context2.Args.AddArg(Execution::Args::Type::AdminSettingDisable, "LocalManifestFiles"sv); - context2.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); - SettingsCommand settings2({}); - settings2.Execute(context2); - INFO(settingsOutput2.str()); - - // Now using local manifests should fail - Execution::Args args3; - args3.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); - InstallCommand installCommand3({}); - REQUIRE_THROWS(installCommand3.ValidateArguments(args3)); + { + // If there's no admin setting, using local manifest should fail. + Execution::Args args; + args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); + InstallCommand installCommand({}); + REQUIRE_THROWS(installCommand.ValidateArguments(args)); + } + + { + // Using settings command to enable local manifests + std::ostringstream settingsOutput; + TestContext context{ settingsOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + context.Args.AddArg(Execution::Args::Type::AdminSettingEnable, "LocalManifestFiles"sv); + context.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); + SettingsCommand settings({}); + settings.Execute(context); + INFO(settingsOutput.str()); + } + + { + // Now using local manifests should succeed + Execution::Args args2; + args2.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); + InstallCommand installCommand2({}); + REQUIRE_NOTHROW(installCommand2.ValidateArguments(args2)); + } + + { + // Using settings command to disable local manifests + std::ostringstream settingsOutput2; + TestContext context2{ settingsOutput2, std::cin }; + auto previousThreadGlobals = context2.SetForCurrentThread(); + context2.Args.AddArg(Execution::Args::Type::AdminSettingDisable, "LocalManifestFiles"sv); + context2.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); + SettingsCommand settings2({}); + settings2.Execute(context2); + INFO(settingsOutput2.str()); + } + + { + // Now using local manifests should fail + Execution::Args args3; + args3.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); + InstallCommand installCommand3({}); + REQUIRE_THROWS(installCommand3.ValidateArguments(args3)); + } } diff --git a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp index 92663e4c4e..bec0fea927 100644 --- a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp +++ b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp @@ -15,7 +15,7 @@ #define AICLI_TraceLoggingWriteActivity(_eventName_,...) TraceLoggingWriteActivity(\ g_hTraceProvider,\ _eventName_,\ -GetActivityId(),\ +s_useGlobalTelemetryActivityId ? &s_globalTelemetryLoggerActivityId : GetActivityId(),\ nullptr,\ TraceLoggingCountedUtf8String(m_caller.c_str(), static_cast(m_caller.size()), "Caller"),\ TraceLoggingPackedFieldEx(m_telemetryCorrelationJsonW.c_str(), static_cast((m_telemetryCorrelationJsonW.size() + 1) * sizeof(wchar_t)), TlgInUNICODESTRING, TlgOutJSON, "CvJson"),\ @@ -44,23 +44,66 @@ namespace AppInstaller::Logging namespace { + // TODO: This and all usages should be removed after transition to summary event in back end. static const uint32_t s_RootExecutionId = 0; - - std::atomic_uint32_t s_executionStage{ 0 }; - - std::atomic_uint32_t s_subExecutionId{ s_RootExecutionId }; + static std::atomic_uint32_t s_subExecutionId{ s_RootExecutionId }; constexpr std::wstring_view s_UserProfileReplacement = L"%USERPROFILE%"sv; + // TODO: Temporary code to keep existing telemetry behavior + static bool s_useGlobalTelemetryActivityId = false; + static GUID s_globalTelemetryLoggerActivityId = GUID_NULL; + void __stdcall wilResultLoggingCallback(const wil::FailureInfo& info) noexcept { Telemetry().LogFailure(info); } + + FailureTypeEnum ConvertWilFailureTypeToFailureType(wil::FailureType failureType) + { + switch (failureType) + { + case wil::FailureType::Exception: + return FailureTypeEnum::ResultException; + case wil::FailureType::Return: + return FailureTypeEnum::ResultReturn; + case wil::FailureType::Log: + return FailureTypeEnum::ResultLog; + case wil::FailureType::FailFast: + return FailureTypeEnum::ResultFailFast; + default: + return FailureTypeEnum::Unknown; + } + } + + std::string_view LogExceptionTypeToString(FailureTypeEnum exceptionType) + { + switch (exceptionType) + { + case FailureTypeEnum::ResultException: + return "wil::ResultException"sv; + case FailureTypeEnum::WinrtHResultError: + return "winrt::hresult_error"sv; + case FailureTypeEnum::ResourceOpen: + return "ResourceOpenException"sv; + case FailureTypeEnum::StdException: + return "std::exception"sv; + case FailureTypeEnum::Unknown: + default: + return "unknown"sv; + } + } + } + + TelemetrySummary::TelemetrySummary(const TelemetrySummary& other) + { + this->IsCOMCall = other.IsCOMCall; } TelemetryTraceLogger::TelemetryTraceLogger() { std::ignore = CoCreateGuid(&m_activityId); + m_subExecutionId = s_RootExecutionId; } const GUID* TelemetryTraceLogger::GetActivityId() const @@ -68,6 +111,11 @@ namespace AppInstaller::Logging return &m_activityId; } + const GUID* TelemetryTraceLogger::GetParentActivityId() const + { + return &m_parentActivityId; + } + bool TelemetryTraceLogger::DisableRuntime() { return m_isRuntimeEnabled.exchange(false); @@ -132,6 +180,23 @@ namespace AppInstaller::Logging m_caller = caller; } + void TelemetryTraceLogger::SetExecutionStage(uint32_t stage) noexcept + { + m_executionStage = stage; + } + + std::unique_ptr TelemetryTraceLogger::CreateSubTraceLogger() const + { + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !this->m_isInitialized); + + auto subTraceLogger = std::make_unique(*this); + + subTraceLogger->m_parentActivityId = this->m_activityId; + subTraceLogger->m_subExecutionId = s_subExecutionId++; + + return subTraceLogger; + } + void TelemetryTraceLogger::LogFailure(const wil::FailureInfo& failure) const noexcept { if (IsTelemetryEnabled()) @@ -140,7 +205,7 @@ namespace AppInstaller::Logging AICLI_TraceLoggingWriteActivity( "FailureInfo", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingHResult(failure.hr, "HResult"), AICLI_TraceLoggingWStringView(anonMessage, "Message"), TraceLoggingString(failure.pszModule, "Module"), @@ -148,9 +213,17 @@ namespace AppInstaller::Logging TraceLoggingUInt32(static_cast(failure.type), "Type"), TraceLoggingString(failure.pszFile, "File"), TraceLoggingUInt32(failure.uLineNumber, "Line"), - TraceLoggingUInt32(s_executionStage, "ExecutionStage"), + TraceLoggingUInt32(m_executionStage, "ExecutionStage"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.FailureHResult = failure.hr; + m_summary.FailureMessage = anonMessage; + m_summary.FailureModule = failure.pszModule; + m_summary.FailureThreadId = failure.threadId; + m_summary.FailureType = ConvertWilFailureTypeToFailureType(failure.type); + m_summary.FailureFile = failure.pszFile; + m_summary.FailureLine = failure.uLineNumber; } // Also send failure to the log @@ -179,6 +252,8 @@ namespace AppInstaller::Logging TraceLoggingCountedString(packageVersion->c_str(), static_cast(packageVersion->size()), "PackageVersion"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.IsCOMCall = isCOMCall; } AICLI_LOG(Core, Info, << "WinGet, version [" << version << "], activity [" << *GetActivityId() << ']'); @@ -200,6 +275,8 @@ namespace AppInstaller::Logging AICLI_TraceLoggingStringView(commandName, "Command"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance | PDT_ProductAndServiceUsage), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.Command = commandName; } AICLI_LOG(CLI, Info, << "Leaf command to execute: " << commandName); @@ -214,6 +291,8 @@ namespace AppInstaller::Logging AICLI_TraceLoggingStringView(commandName, "Command"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.CommandSuccess = true; } AICLI_LOG(CLI, Info, << "Leaf command succeeded: " << commandName); @@ -225,35 +304,45 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "CommandTermination", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingHResult(hr, "HResult"), AICLI_TraceLoggingStringView(file, "File"), TraceLoggingUInt64(static_cast(line), "Line"), - TraceLoggingUInt32(s_executionStage, "ExecutionStage"), + TraceLoggingUInt32(m_executionStage, "ExecutionStage"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.FailureHResult = hr; + m_summary.FailureType = FailureTypeEnum::CommandTermination; + m_summary.FailureFile = file; + m_summary.FailureLine = static_cast(line); } AICLI_LOG(CLI, Error, << "Terminating context: 0x" << SetHRFormat << hr << " at " << file << ":" << line); } - void TelemetryTraceLogger::LogException(std::string_view type, std::string_view message) const noexcept + void TelemetryTraceLogger::LogException(FailureTypeEnum type, std::string_view message) const noexcept { + auto exceptionTypeString = LogExceptionTypeToString(type); + if (IsTelemetryEnabled()) { auto anonMessage = AnonymizeString(Utility::ConvertToUTF16(message)); AICLI_TraceLoggingWriteActivity( "Exception", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), - AICLI_TraceLoggingStringView(type, "Type"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), + AICLI_TraceLoggingStringView(exceptionTypeString, "Type"), AICLI_TraceLoggingWStringView(anonMessage, "Message"), - TraceLoggingUInt32(s_executionStage, "ExecutionStage"), + TraceLoggingUInt32(m_executionStage, "ExecutionStage"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.FailureType = type; + m_summary.FailureMessage = anonMessage; } - AICLI_LOG(CLI, Error, << "Caught " << type << ": " << message); + AICLI_LOG(CLI, Error, << "Caught " << exceptionTypeString << ": " << message); } void TelemetryTraceLogger::LogIsManifestLocal(bool isLocalManifest) const noexcept @@ -262,10 +351,12 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "GetManifest", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingBool(isLocalManifest, "IsManifestLocal"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.IsManifestLocal = isLocalManifest; } } @@ -275,12 +366,16 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "ManifestFields", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(name, "Name"), AICLI_TraceLoggingStringView(version, "Version"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.PackageIdentifier = id; + m_summary.PackageName = name; + m_summary.PackageVersion = version; } AICLI_LOG(CLI, Info, << "Manifest fields: Name [" << name << "], Version [" << version << ']'); @@ -292,7 +387,7 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "NoAppMatch", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); } @@ -306,7 +401,7 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "MultiAppMatch", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); } @@ -320,11 +415,14 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "AppFound", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(name, "Name"), AICLI_TraceLoggingStringView(id, "Id"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.PackageIdentifier = id; + m_summary.PackageName = name; } AICLI_LOG(CLI, Info, << "Found one app. App id: " << id << " App name: " << name); @@ -336,7 +434,7 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "SelectedInstaller", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingInt32(arch, "Arch"), AICLI_TraceLoggingStringView(url, "Url"), AICLI_TraceLoggingStringView(installerType, "InstallerType"), @@ -344,6 +442,12 @@ namespace AppInstaller::Logging AICLI_TraceLoggingStringView(language, "Language"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.InstallerArchitecture = arch; + m_summary.InstallerUrl = url; + m_summary.InstallerType = installerType; + m_summary.InstallerScope = scope; + m_summary.InstallerLocale = language; } AICLI_LOG(CLI, Info, << "Completed installer selection."); @@ -369,7 +473,7 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "SearchRequest", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(type, "Type"), AICLI_TraceLoggingStringView(query, "Query"), AICLI_TraceLoggingStringView(id, "Id"), @@ -381,6 +485,16 @@ namespace AppInstaller::Logging AICLI_TraceLoggingStringView(request, "Request"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.SearchType = type; + m_summary.SearchQuery = query; + m_summary.SearchId = id; + m_summary.SearchName = name; + m_summary.SearchMoniker = moniker; + m_summary.SearchTag = tag; + m_summary.SearchCommand = command; + m_summary.SearchMaximum = static_cast(maximum); + m_summary.SearchRequest = request; } } @@ -390,10 +504,12 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "SearchResultCount", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingUInt64(resultCount, "ResultCount"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.SearchResultCount = resultCount; } } @@ -409,15 +525,22 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "HashMismatch", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), AICLI_TraceLoggingStringView(channel, "Channel"), TraceLoggingBinary(expected.data(), static_cast(expected.size()), "Expected"), TraceLoggingBinary(actual.data(), static_cast(actual.size()), "Actual"), - TraceLoggingValue(overrideHashMismatch, "Override"), + TraceLoggingBool(overrideHashMismatch, "Override"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.PackageIdentifier = id; + m_summary.PackageVersion = version; + m_summary.Channel = channel; + m_summary.HashMismatchExpected = expected; + m_summary.HashMismatchActual = actual; + m_summary.HashMismatchOverride = overrideHashMismatch; } AICLI_LOG(CLI, Error, @@ -434,7 +557,7 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "InstallerFailure", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), AICLI_TraceLoggingStringView(channel, "Channel"), @@ -442,6 +565,12 @@ namespace AppInstaller::Logging TraceLoggingUInt32(errorCode, "ErrorCode"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.PackageIdentifier = id; + m_summary.PackageVersion = version; + m_summary.Channel = channel; + m_summary.InstallerExecutionType = type; + m_summary.InstallerErrorCode = errorCode; } AICLI_LOG(CLI, Error, << type << " installer failed: " << errorCode); @@ -453,13 +582,18 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "UninstallerFailure", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), AICLI_TraceLoggingStringView(type, "Type"), TraceLoggingUInt32(errorCode, "ErrorCode"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.PackageIdentifier = id; + m_summary.PackageVersion = version; + m_summary.UninstallerExecutionType = type; + m_summary.UninstallerErrorCode = errorCode; } AICLI_LOG(CLI, Error, << type << " uninstaller failed: " << errorCode); @@ -491,7 +625,7 @@ namespace AppInstaller::Logging AICLI_TraceLoggingWriteActivity( "InstallARPChange", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(sourceIdentifier, "SourceIdentifier"), AICLI_TraceLoggingStringView(packageIdentifier, "PackageIdentifier"), AICLI_TraceLoggingStringView(packageVersion, "PackageVersion"), @@ -505,6 +639,18 @@ namespace AppInstaller::Logging TraceLoggingUInt64(static_cast(languageNumber), "ARPLanguage"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance | PDT_ProductAndServiceUsage | PDT_SoftwareSetupAndInventory), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + + m_summary.SourceIdentifier = sourceIdentifier; + m_summary.PackageIdentifier = packageIdentifier; + m_summary.PackageVersion = packageVersion; + m_summary.Channel = packageChannel; + m_summary.ChangesToARP = static_cast(changesToARP); + m_summary.MatchesInARP = static_cast(matchesInARP); + m_summary.ChangesThatMatch = static_cast(countOfIntersectionOfChangesAndMatches); + m_summary.ARPName = arpName; + m_summary.ARPVersion = arpVersion; + m_summary.ARPPublisher = arpPublisher; + m_summary.ARPLanguage = static_cast(languageNumber); } AICLI_LOG(CLI, Info, << "During package install, " << changesToARP << " changes to ARP were observed, " @@ -526,14 +672,93 @@ namespace AppInstaller::Logging { AICLI_TraceLoggingWriteActivity( "NonFatalDOError", - TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(url, "Url"), TraceLoggingHResult(hr, "HResult"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)); + + m_summary.DOUrl = url; + m_summary.DOHResult = hr; } } + TelemetryTraceLogger::~TelemetryTraceLogger() + { + if (IsTelemetryEnabled()) + { + LocIndString version = Runtime::GetClientVersion(); + LocIndString packageVersion; + if (Runtime::IsRunningInPackagedContext()) + { + packageVersion = Runtime::GetPackageVersion(); + } + + TraceLoggingWriteActivity( + g_hTraceProvider, + "Summary", + GetActivityId(), + GetParentActivityId(), + // From member fields or program info. + AICLI_TraceLoggingStringView(m_caller, "Caller"), + TraceLoggingPackedFieldEx(m_telemetryCorrelationJsonW.c_str(), static_cast((m_telemetryCorrelationJsonW.size() + 1) * sizeof(wchar_t)), TlgInUNICODESTRING, TlgOutJSON, "CvJson"), + TraceLoggingCountedString(version->c_str(), static_cast(version->size()), "ClientVersion"), + TraceLoggingCountedString(packageVersion->c_str(), static_cast(packageVersion->size()), "PackageVersion"), + TraceLoggingBool(Runtime::IsReleaseBuild(), "IsReleaseBuild"), + TraceLoggingUInt32(m_executionStage, "ExecutionStage"), + // From TelemetrySummary + TraceLoggingHResult(m_summary.FailureHResult, "FailureHResult"), + AICLI_TraceLoggingWStringView(m_summary.FailureMessage, "FailureMessage"), + AICLI_TraceLoggingStringView(m_summary.FailureModule, "FailureModule"), + TraceLoggingUInt32(m_summary.FailureThreadId, "FailureThreadId"), + TraceLoggingUInt32(static_cast(m_summary.FailureType), "FailureType"), + AICLI_TraceLoggingStringView(m_summary.FailureFile, "FailureFile"), + TraceLoggingUInt32(m_summary.FailureLine, "FailureLine"), + TraceLoggingBool(m_summary.IsCOMCall, "IsCOMCall"), + AICLI_TraceLoggingStringView(m_summary.Command, "Command"), + TraceLoggingBool(m_summary.CommandSuccess, "CommandSuccess"), + TraceLoggingBool(m_summary.IsManifestLocal, "IsManifestLocal"), + AICLI_TraceLoggingStringView(m_summary.PackageIdentifier, "PackageIdentifier"), + AICLI_TraceLoggingStringView(m_summary.PackageName, "PackageName"), + AICLI_TraceLoggingStringView(m_summary.PackageVersion, "PackageVersion"), + AICLI_TraceLoggingStringView(m_summary.Channel, "Channel"), + AICLI_TraceLoggingStringView(m_summary.SourceIdentifier, "SourceIdentifier"), + TraceLoggingInt32(m_summary.InstallerArchitecture, "InstallerArchitecture"), + AICLI_TraceLoggingStringView(m_summary.InstallerUrl, "InstallerUrl"), + AICLI_TraceLoggingStringView(m_summary.InstallerType, "InstallerType"), + AICLI_TraceLoggingStringView(m_summary.InstallerScope, "InstallerScope"), + AICLI_TraceLoggingStringView(m_summary.InstallerLocale, "InstallerLocale"), + AICLI_TraceLoggingStringView(m_summary.SearchType, "SearchType"), + AICLI_TraceLoggingStringView(m_summary.SearchQuery, "SearchQuery"), + AICLI_TraceLoggingStringView(m_summary.SearchId, "SearchId"), + AICLI_TraceLoggingStringView(m_summary.SearchName, "SearchName"), + AICLI_TraceLoggingStringView(m_summary.SearchMoniker, "SearchMoniker"), + AICLI_TraceLoggingStringView(m_summary.SearchTag, "SearchTag"), + AICLI_TraceLoggingStringView(m_summary.SearchCommand, "SearchCommand"), + TraceLoggingUInt64(m_summary.SearchMaximum, "SearchMaximum"), + AICLI_TraceLoggingStringView(m_summary.SearchRequest, "SearchRequest"), + TraceLoggingUInt64(m_summary.SearchResultCount, "SearchResultCount"), + TraceLoggingBinary(m_summary.HashMismatchExpected.data(), static_cast(m_summary.HashMismatchExpected.size()), "HashMismatchExpected"), + TraceLoggingBinary(m_summary.HashMismatchActual.data(), static_cast(m_summary.HashMismatchActual.size()), "HashMismatchActual"), + TraceLoggingBool(m_summary.HashMismatchOverride, "HashMismatchOverride"), + AICLI_TraceLoggingStringView(m_summary.InstallerExecutionType, "InstallerExecutionType"), + TraceLoggingUInt32(m_summary.InstallerErrorCode, "InstallerErrorCode"), + AICLI_TraceLoggingStringView(m_summary.UninstallerExecutionType, "UninstallerExecutionType"), + TraceLoggingUInt32(m_summary.UninstallerErrorCode, "UninstallerErrorCode"), + TraceLoggingUInt64(m_summary.ChangesToARP, "ChangesToARP"), + TraceLoggingUInt64(m_summary.MatchesInARP, "MatchesInARP"), + TraceLoggingUInt64(m_summary.ChangesThatMatch, "ChangesThatMatch"), + TraceLoggingUInt64(m_summary.ARPLanguage, "ARPLanguage"), + AICLI_TraceLoggingStringView(m_summary.ARPName, "ARPName"), + AICLI_TraceLoggingStringView(m_summary.ARPVersion, "ARPVersion"), + AICLI_TraceLoggingStringView(m_summary.ARPPublisher, "ARPPublisher"), + AICLI_TraceLoggingStringView(m_summary.DOUrl, "DOUrl"), + TraceLoggingHResult(m_summary.DOHResult, "DOHResult"), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance | PDT_ProductAndServiceUsage | PDT_SoftwareSetupAndInventory), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)); + } + } + bool TelemetryTraceLogger::IsTelemetryEnabled() const noexcept { return g_IsTelemetryProviderEnabled && m_isInitialized && m_isSettingEnabled && m_isRuntimeEnabled; @@ -587,6 +812,12 @@ namespace AppInstaller::Logging wil::SetResultLoggingCallback(wilResultLoggingCallback); } + void UseGlobalTelemetryLoggerActivityIdOnly() + { + s_useGlobalTelemetryActivityId = true; + std::ignore = CoCreateGuid(&s_globalTelemetryLoggerActivityId); + } + DisableTelemetryScope::DisableTelemetryScope() { m_token = Telemetry().DisableRuntime(); @@ -600,42 +831,11 @@ namespace AppInstaller::Logging } } - void SetExecutionStage(uint32_t stage) - { - s_executionStage = stage; - } - - std::atomic_uint32_t SubExecutionTelemetryScope::m_sessionId{ s_RootExecutionId }; - - SubExecutionTelemetryScope::SubExecutionTelemetryScope() - { - auto expected = s_RootExecutionId; - THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !s_subExecutionId.compare_exchange_strong(expected, ++m_sessionId), - "Cannot create a sub execution telemetry session when a previous session exists."); - } - - SubExecutionTelemetryScope::SubExecutionTelemetryScope(uint32_t sessionId) - { - auto expected = s_RootExecutionId; - THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !s_subExecutionId.compare_exchange_strong(expected, sessionId), - "Cannot create a sub execution telemetry session when a previous session exists."); - } - - uint32_t SubExecutionTelemetryScope::GetCurrentSubExecutionId() const - { - return (uint32_t)s_subExecutionId; - } - - SubExecutionTelemetryScope::~SubExecutionTelemetryScope() - { - s_subExecutionId = s_RootExecutionId; - } - #ifndef AICLI_DISABLE_TEST_HOOKS // Replace this test hook with context telemetry when it gets moved over void TestHook_SetTelemetryOverride(std::shared_ptr ttl) { s_TelemetryTraceLogger_TestOverride = std::move(ttl); } -#endif +#endif } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerLanguageUtilities.h b/src/AppInstallerCommonCore/Public/AppInstallerLanguageUtilities.h index 0b673d346c..2a8f2c92fb 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerLanguageUtilities.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerLanguageUtilities.h @@ -152,3 +152,13 @@ std::enable_if_t, std::ostream&> operator<<(std::ostream& out, { return out << AppInstaller::ToIntegral(e); } + +template +struct CopyConstructibleAtomic : public std::atomic +{ + using std::atomic::atomic; + using std::atomic::operator=; + + CopyConstructibleAtomic(const CopyConstructibleAtomic& other) : + std::atomic(other.load()) {} +}; diff --git a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h index 37fda40f8f..5c8da57590 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h @@ -15,6 +15,115 @@ namespace AppInstaller::Settings namespace AppInstaller::Logging { + enum class FailureTypeEnum : UINT32 + { + None = 0x0, + + // Failure type from FailureInfo in result_macros.h + ResultException = 0x1, // THROW_... + ResultReturn = 0x2, // RETURN_..._LOG or RETURN_..._MSG + ResultLog = 0x3, // LOG_... + ResultFailFast = 0x4, // FAIL_FAST_... + + // Other failure types from LogException() + Unknown = 0x10000, + WinrtHResultError = 0x10001, + ResourceOpen = 0x10002, + StdException = 0x10003, + + // Command termination + CommandTermination = 0x20000, + }; + + // Contains all fields logged through the TelemetryTraceLogger. Last write wins. + // This will be used to report a summary event upon destruction of the TelemetryTraceLogger. + struct TelemetrySummary + { + TelemetrySummary() = default; + + // Selectively copy member fields for copy constructor; + TelemetrySummary(const TelemetrySummary& other); + TelemetrySummary& operator=(const TelemetrySummary&) = default; + + TelemetrySummary(TelemetrySummary&&) = default; + TelemetrySummary& operator=(TelemetrySummary&&) = default; + + // Log wil failure, exception, command termination + HRESULT FailureHResult = S_OK; + std::wstring FailureMessage; + std::string FailureModule; + UINT32 FailureThreadId = 0; + FailureTypeEnum FailureType = FailureTypeEnum::None; + std::string FailureFile; + UINT32 FailureLine = 0; + + // LogStartup + bool IsCOMCall = false; + + // LogCommand + std::string Command; + + // LogCommandSuccess + bool CommandSuccess = false; + + // LogIsManifestLocal + bool IsManifestLocal = false; + + // LogManifestFields, LogAppFound + std::string PackageIdentifier; + std::string PackageName; + std::string PackageVersion; + std::string Channel; + std::string SourceIdentifier; + + // LogSelectedInstaller + INT32 InstallerArchitecture = -1; + std::string InstallerUrl; + std::string InstallerType; + std::string InstallerScope; + std::string InstallerLocale; + + // LogSearchRequest + std::string SearchType; + std::string SearchQuery; + std::string SearchId; + std::string SearchName; + std::string SearchMoniker; + std::string SearchTag; + std::string SearchCommand; + UINT64 SearchMaximum = 0; + std::string SearchRequest; + + // LogSearchResultCount + UINT64 SearchResultCount = 0; + + // LogInstallerHashMismatch + std::vector HashMismatchExpected; + std::vector HashMismatchActual; + bool HashMismatchOverride = false; + + // LogInstallerFailure + std::string InstallerExecutionType; + UINT32 InstallerErrorCode = 0; + + // LogUninstallerFailure + std::string UninstallerExecutionType; + UINT32 UninstallerErrorCode = 0; + + // LogSuccessfulInstallARPChange + UINT64 ChangesToARP = 0; + UINT64 MatchesInARP = 0; + UINT64 ChangesThatMatch = 0; + UINT64 ARPLanguage = 0; + std::string ARPName; + std::string ARPVersion; + std::string ARPPublisher; + + // LogNonFatalDOError + std::string DOUrl; + HRESULT DOHResult = S_OK; + }; + // This type contains the registration lifetime of the telemetry trace logging provider. // Due to the nature of trace logging, specific methods should be added per desired trace. // As there should not be a significantly large number of individual telemetry events, @@ -23,7 +132,7 @@ namespace AppInstaller::Logging { TelemetryTraceLogger(); - ~TelemetryTraceLogger() = default; + ~TelemetryTraceLogger(); TelemetryTraceLogger(const TelemetryTraceLogger&) = default; TelemetryTraceLogger& operator=(const TelemetryTraceLogger&) = default; @@ -38,6 +147,9 @@ namespace AppInstaller::Logging // Return address of m_activityId const GUID* GetActivityId() const; + // Return address of m_parentActivityId + const GUID* GetParentActivityId() const; + // Capture if UserSettings is enabled and set user profile path void Initialize(); @@ -50,6 +162,10 @@ namespace AppInstaller::Logging // Store the passed in Telemetry Correlation Json for COM calls void SetTelemetryCorrelationJson(const std::wstring_view jsonStr_view) noexcept; + void SetExecutionStage(uint32_t stage) noexcept; + + std::unique_ptr CreateSubTraceLogger() const; + // Logs the failure info. void LogFailure(const wil::FailureInfo& failure) const noexcept; @@ -66,7 +182,7 @@ namespace AppInstaller::Logging void LogCommandTermination(HRESULT hr, std::string_view file, size_t line) const noexcept; // Logs the invoked command termination. - void LogException(std::string_view type, std::string_view message) const noexcept; + void LogException(FailureTypeEnum type, std::string_view message) const noexcept; // Logs whether the manifest used in workflow is local void LogIsManifestLocal(bool isLocalManifest) const noexcept; @@ -147,15 +263,23 @@ namespace AppInstaller::Logging std::wstring AnonymizeString(std::wstring_view input) const noexcept; bool m_isSettingEnabled = true; - std::atomic_bool m_isRuntimeEnabled{ true }; - std::atomic_bool m_isInitialized{ false }; + CopyConstructibleAtomic m_isRuntimeEnabled{ true }; + CopyConstructibleAtomic m_isInitialized{ false }; + + CopyConstructibleAtomic m_executionStage{ 0 }; GUID m_activityId = GUID_NULL; + GUID m_parentActivityId = GUID_NULL; std::wstring m_telemetryCorrelationJsonW = L"{}"; std::string m_caller; // Data that is needed by AnonymizeString std::wstring m_userProfile; + + mutable TelemetrySummary m_summary; + + // TODO: This and all related code could be removed after transition to summary event in back end. + uint32_t m_subExecutionId; }; // Helper to make the call sites look clean. @@ -164,6 +288,9 @@ namespace AppInstaller::Logging // Turns on wil failure telemetry and logging. void EnableWilFailureTelemetry(); + // TODO: Temporary code to keep existing telemetry behavior for command execution cases. + void UseGlobalTelemetryLoggerActivityIdOnly(); + // An RAII object to disable telemetry during its lifetime. // Primarily used by the complete command to prevent messy input from spamming us. struct DisableTelemetryScope @@ -181,29 +308,4 @@ namespace AppInstaller::Logging private: DestructionToken m_token; }; - - // Sets an execution stage to be reported when failures occur. - void SetExecutionStage(uint32_t stage); - - // An RAII object to log telemetry as sub execution. - // Does not support nested sub execution. - struct SubExecutionTelemetryScope - { - SubExecutionTelemetryScope(); - - SubExecutionTelemetryScope(uint32_t sessionId); - - SubExecutionTelemetryScope(const SubExecutionTelemetryScope&) = delete; - SubExecutionTelemetryScope& operator=(const SubExecutionTelemetryScope&) = delete; - - SubExecutionTelemetryScope(SubExecutionTelemetryScope&&) = default; - SubExecutionTelemetryScope& operator=(SubExecutionTelemetryScope&&) = default; - - uint32_t GetCurrentSubExecutionId() const; - - ~SubExecutionTelemetryScope(); - - private: - static std::atomic_uint32_t m_sessionId; - }; } diff --git a/src/AppInstallerCommonCore/Public/winget/ThreadGlobals.h b/src/AppInstallerCommonCore/Public/winget/ThreadGlobals.h index 83864f0e71..aa07178400 100644 --- a/src/AppInstallerCommonCore/Public/winget/ThreadGlobals.h +++ b/src/AppInstallerCommonCore/Public/winget/ThreadGlobals.h @@ -14,6 +14,10 @@ namespace AppInstaller::ThreadLocalStorage ThreadGlobals() = default; ~ThreadGlobals() = default; + // Request that a sub ThreadGlobals be constructed from the given parent. + struct create_sub_thread_globals_t {}; + ThreadGlobals(ThreadGlobals& parent, create_sub_thread_globals_t); + AppInstaller::Logging::DiagnosticLogger& GetDiagnosticLogger(); AppInstaller::Logging::TelemetryTraceLogger& GetTelemetryLogger(); @@ -29,9 +33,9 @@ namespace AppInstaller::ThreadLocalStorage void Initialize(); - std::unique_ptr m_pDiagnosticLogger; + std::shared_ptr m_pDiagnosticLogger; std::unique_ptr m_pTelemetryLogger; - std::once_flag loggerInitOnceFlag; + std::once_flag m_loggerInitOnceFlag; }; struct PreviousThreadGlobals diff --git a/src/AppInstallerCommonCore/ThreadGlobals.cpp b/src/AppInstallerCommonCore/ThreadGlobals.cpp index 2853ee50df..13bec7c1fd 100644 --- a/src/AppInstallerCommonCore/ThreadGlobals.cpp +++ b/src/AppInstallerCommonCore/ThreadGlobals.cpp @@ -6,13 +6,22 @@ namespace AppInstaller::ThreadLocalStorage using namespace AppInstaller::Logging; // Set and return Globals for Current Thread - static ThreadGlobals* SetOrGetThreadGlobals(bool setThreadGlobals, ThreadGlobals* pThreadGlobals = nullptr); - + static ThreadGlobals* SetOrGetThreadGlobals(bool setThreadGlobals, ThreadGlobals* pThreadGlobals = nullptr); + + ThreadGlobals::ThreadGlobals(ThreadGlobals& parent, create_sub_thread_globals_t) + { + parent.Initialize(); + m_pDiagnosticLogger = parent.m_pDiagnosticLogger; + m_pTelemetryLogger = parent.m_pTelemetryLogger->CreateSubTraceLogger(); + // Flip the initialization flag + std::call_once(m_loggerInitOnceFlag, []() {}); + } + DiagnosticLogger& ThreadGlobals::GetDiagnosticLogger() { return *(m_pDiagnosticLogger); - } - + } + TelemetryTraceLogger& ThreadGlobals::GetTelemetryLogger() { return *(m_pTelemetryLogger); @@ -31,7 +40,7 @@ namespace AppInstaller::ThreadLocalStorage { try { - std::call_once(loggerInitOnceFlag, [this]() + std::call_once(m_loggerInitOnceFlag, [this]() { m_pDiagnosticLogger = std::make_unique(); m_pTelemetryLogger = std::make_unique(); diff --git a/src/AppInstallerCommonCore/TraceLogger.cpp b/src/AppInstallerCommonCore/TraceLogger.cpp index f77f7b9821..f11f6849d1 100644 --- a/src/AppInstallerCommonCore/TraceLogger.cpp +++ b/src/AppInstallerCommonCore/TraceLogger.cpp @@ -16,7 +16,7 @@ namespace AppInstaller::Logging TraceLoggingWriteActivity(g_hTraceProvider, "Diagnostics", Telemetry().GetActivityId(), - nullptr, + Telemetry().GetParentActivityId(), TraceLoggingString(strstr.str().c_str(), "LogMessage")); } catch (...) @@ -29,7 +29,7 @@ namespace AppInstaller::Logging TraceLoggingWriteActivity(g_hTraceProvider, "Diagnostics", Telemetry().GetActivityId(), - nullptr, + Telemetry().GetParentActivityId(), TraceLoggingCountedUtf8String(message.data(), static_cast(message.size()), "LogMessage")); } catch (...)