diff --git a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp index 448e33d52b..afd190cac2 100644 --- a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp +++ b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp @@ -11,6 +11,7 @@ #define AICLI_TraceLoggingStringView(_sv_,_name_) TraceLoggingCountedUtf8String(_sv_.data(), static_cast(_sv_.size()), _name_) #define AICLI_TraceLoggingWStringView(_sv_,_name_) TraceLoggingCountedWideString(_sv_.data(), static_cast(_sv_.size()), _name_) +#define AICLI_TraceLoggingJsonWString(_sv_, _name_) TraceLoggingPackedFieldEx(_sv_.c_str(), static_cast((_sv_.size() + 1) * sizeof(wchar_t)), TlgInUNICODESTRING, TlgOutJSON, _name_) #define AICLI_TraceLoggingWriteActivity(_eventName_,...) TraceLoggingWriteActivity(\ g_hTraceProvider,\ @@ -18,12 +19,13 @@ _eventName_,\ 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"),\ +AICLI_TraceLoggingJsonWString(m_telemetryCorrelationJsonW, "CvJson"),\ __VA_ARGS__) namespace AppInstaller::Logging { using namespace Utility; + using namespace Settings; namespace { @@ -77,6 +79,25 @@ namespace AppInstaller::Logging return "unknown"sv; } } + + std::wstring GetExperimentsJson(const std::map& experiments) + { + std::wstringstream ss; + ss << "{"; + auto first = 0; + for (const auto& experiment : experiments) + { + if (first++ != 0) + { + ss << L","; + } + + ss << L"\"" << Utility::ConvertToUTF16(Experiment::GetExperiment(experiment.first).JsonName()) + << L"\":" << experiment.second.ToJson(); + } + ss << L"}"; + return ss.str(); + } } TelemetrySummary::TelemetrySummary(const TelemetrySummary& other) @@ -760,6 +781,20 @@ namespace AppInstaller::Logging AICLI_LOG(CLI, Error, << type << " repair failed: " << errorCode); } + Settings::ExperimentState TelemetryTraceLogger::GetExperimentState(Experiment::Key key) + { +#ifndef AICLI_DISABLE_TEST_HOOKS + m_summary.Experiments.clear(); +#endif + + if (m_summary.Experiments.find(key) == m_summary.Experiments.end()) + { + m_summary.Experiments[key] = Experiment::GetStateInternal(key); + } + + return m_summary.Experiments[key]; + } + TelemetryTraceLogger::~TelemetryTraceLogger() { if (IsTelemetryEnabled()) @@ -773,6 +808,8 @@ namespace AppInstaller::Logging if (m_useSummary) { + auto experimentsJson = GetExperimentsJson(m_summary.Experiments); + TraceLoggingWriteActivity( g_hTraceProvider, "SummaryV2", @@ -780,7 +817,7 @@ namespace AppInstaller::Logging 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"), + AICLI_TraceLoggingJsonWString(m_telemetryCorrelationJsonW, "CvJson"), TraceLoggingCountedString(version->c_str(), static_cast(version->size()), "ClientVersion"), TraceLoggingCountedString(packageVersion->c_str(), static_cast(packageVersion->size()), "ClientPackageVersion"), TraceLoggingBool(Runtime::IsReleaseBuild(), "IsReleaseBuild"), @@ -838,7 +875,8 @@ namespace AppInstaller::Logging AICLI_TraceLoggingStringView(m_summary.RepairExecutionType, "RepairExecutionType"), TraceLoggingUInt32(m_summary.RepairErrorCode, "RepairErrorCode"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance | PDT_ProductAndServiceUsage | PDT_SoftwareSetupAndInventory), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)); + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + AICLI_TraceLoggingJsonWString(experimentsJson, "ExperimentsJson")); } } } diff --git a/src/AppInstallerCommonCore/Experiment.cpp b/src/AppInstallerCommonCore/Experiment.cpp index 9b01743a72..8a9c006fb4 100644 --- a/src/AppInstallerCommonCore/Experiment.cpp +++ b/src/AppInstallerCommonCore/Experiment.cpp @@ -5,6 +5,7 @@ #include "winget/Experiment.h" #include "winget/UserSettings.h" #include "Experiment/Experiment.h" +#include "AppInstallerTelemetry.h" namespace AppInstaller::Settings { @@ -45,24 +46,22 @@ namespace AppInstaller::Settings } } - // Define static members - std::map Experiment::m_experimentStateCache; - std::mutex Experiment::m_mutex; - - ExperimentState Experiment::GetState(Key key) + std::wstring ExperimentState::ToJson() const { - std::lock_guard lock(m_mutex); - -#ifndef AICLI_DISABLE_TEST_HOOKS - m_experimentStateCache.clear(); -#endif + std::wstringstream ss; + ss << L"{\"IsEnabled\":" << (m_isEnabled ? L"true" : L"false") + << L",\"ToggleSource\":" << m_toggleSource << L"}"; + return ss.str(); + } - if (m_experimentStateCache.find(key) == m_experimentStateCache.end()) - { - m_experimentStateCache[key] = GetExperimentStateInternal(key, User()); - } + ExperimentState Experiment::GetStateInternal(Key key) + { + return GetExperimentStateInternal(key, User()); + } - return m_experimentStateCache[key]; + ExperimentState Experiment::GetState(Key key) + { + return Logging::Telemetry().GetExperimentState(key); } Experiment Experiment::GetExperiment(Key key) diff --git a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h index 1ff5962426..cc5bb759c4 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h @@ -3,6 +3,7 @@ #pragma once #include #include +#include #include #include @@ -129,7 +130,8 @@ namespace AppInstaller::Logging std::string DOUrl; HRESULT DOHResult = S_OK; - // TODO Add experiments telemetry + // LogExperiment + std::map Experiments; }; // This type contains the registration lifetime of the telemetry trace logging provider. @@ -269,6 +271,8 @@ namespace AppInstaller::Logging void LogNonFatalDOError(std::string_view url, HRESULT hr) const noexcept; + Settings::ExperimentState GetExperimentState(Settings::Experiment::Key key); + protected: bool IsTelemetryEnabled() const noexcept; diff --git a/src/AppInstallerCommonCore/Public/winget/Experiment.h b/src/AppInstallerCommonCore/Public/winget/Experiment.h index f8def7177f..85981d0a47 100644 --- a/src/AppInstallerCommonCore/Public/winget/Experiment.h +++ b/src/AppInstallerCommonCore/Public/winget/Experiment.h @@ -20,6 +20,7 @@ namespace AppInstaller::Settings ExperimentState(bool isEnabled, ExperimentToggleSource toggleSource) : m_isEnabled(isEnabled), m_toggleSource(toggleSource) {} bool IsEnabled() const { return m_isEnabled; } ExperimentToggleSource ToggleSource() const { return m_toggleSource; } + std::wstring ToJson() const; private: ExperimentToggleSource m_toggleSource; bool m_isEnabled; @@ -44,6 +45,7 @@ namespace AppInstaller::Settings m_name(name), m_jsonName(jsonName), m_link(link), m_key(key) {} static ExperimentState GetState(Key feature); + static ExperimentState GetStateInternal(Key feature); static Experiment GetExperiment(Key key); static std::vector GetAllExperiments(); @@ -57,7 +59,5 @@ namespace AppInstaller::Settings Utility::LocIndView m_jsonName; std::string_view m_link; std::string m_key; - static std::map m_experimentStateCache; - static std::mutex m_mutex; }; }