Telemetry.cpp (16794B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "Telemetry.h" 5 #include "TraceLogging.h" 6 #include <AppInstallerErrors.h> 7 #include <AppInstallerLogging.h> 8 #include <AppInstallerStrings.h> 9 #include <winget/Runtime.h> 10 11 #define AICLI_TraceLoggingStringView(_sv_,_name_) TraceLoggingCountedUtf8String(_sv_.data(), static_cast<ULONG>(_sv_.size()), _name_) 12 #define AICLI_TraceLoggingWStringView(_sv_,_name_) TraceLoggingCountedWideString(_sv_.data(), static_cast<ULONG>(_sv_.size()), _name_) 13 14 #define AICLI_TraceLoggingProcessingSummaryForIntent(_forIntent_,_name_,_pluralName_) \ 15 TraceLoggingUInt32(_forIntent_.Count, _name_ ## "Count"), \ 16 TraceLoggingUInt32(_forIntent_.Run, _pluralName_ ## "Run"), \ 17 TraceLoggingUInt32(_forIntent_.Failed, _pluralName_ ## "Failed") 18 19 #define AICLI_TraceLoggingWriteActivity(_eventName_,...) TraceLoggingWriteActivity(\ 20 g_hTraceProvider,\ 21 _eventName_,\ 22 GetActivityId(),\ 23 nullptr,\ 24 TraceLoggingCountedUtf8String(m_version.c_str(), static_cast<ULONG>(m_version.size()), "CodeVersion"),\ 25 TraceLoggingCountedUtf8String(m_caller.c_str(), static_cast<ULONG>(m_caller.size()), "Caller"),\ 26 __VA_ARGS__) 27 28 #ifdef AICLI_DISABLE_TEST_HOOKS 29 30 #define WinGet_EventItem(_value_,_name_) 31 #define WinGet_SummaryForIntentItem(_forIntent_,_name_,_pluralName_) 32 #define WinGet_WriteEventToDiagnostics(_eventName_,...) 33 34 #else 35 36 struct WinGetAbsorbVA_ARGSCommas 37 { 38 WinGetAbsorbVA_ARGSCommas(int, int) {} 39 }; 40 41 inline std::ostream& operator<<(std::ostream& out, const WinGetAbsorbVA_ARGSCommas&) { return out; } 42 inline std::ostream& operator<<(std::ostream& out, std::wstring_view value) { out << AppInstaller::Utility::ConvertToUTF8(value); return out; } 43 44 #define WinGet_EventItem(_value_,_name_) \ 45 0) << (_name_) << ": " << (_value_) << '\n' << WinGetAbsorbVA_ARGSCommas(0 46 47 #define WinGet_SummaryForIntentItem(_forIntent_,_name_,_pluralName_) \ 48 WinGet_EventItem(_forIntent_.Count, _name_ ## "Count"), \ 49 WinGet_EventItem(_forIntent_.Run, _pluralName_ ## "Run"), \ 50 WinGet_EventItem(_forIntent_.Failed, _pluralName_ ## "Failed") 51 52 #define WinGet_WriteEventToDiagnostics(_eventName_,...) \ 53 { \ 54 std::ostringstream _debugEventStream; \ 55 _debugEventStream << \ 56 "#DebugEventStream\n" << \ 57 "Event: " << (_eventName_) << '\n' << \ 58 "ActivityID: " << *GetActivityId() << '\n' << \ 59 "CodeVersion: " << m_version << '\n' << \ 60 "Caller: " << m_caller << '\n' \ 61 << WinGetAbsorbVA_ARGSCommas(0, __VA_ARGS__ ,0) \ 62 ; \ 63 AICLI_LOG_LARGE_STRING(Config, Verbose, , _debugEventStream.str()); \ 64 } 65 66 #endif 67 68 using namespace std::string_view_literals; 69 70 namespace winrt::Microsoft::Management::Configuration::implementation 71 { 72 namespace 73 { 74 // The data collected from running through a set of results. 75 struct ConfigRunSummaryData 76 { 77 hresult Result = S_OK; 78 ConfigurationUnitResultSource FailurePoint = ConfigurationUnitResultSource::None; 79 TelemetryTraceLogger::ProcessingSummaryForIntent AssertSummary{ ConfigurationUnitIntent::Assert }; 80 TelemetryTraceLogger::ProcessingSummaryForIntent InformSummary{ ConfigurationUnitIntent::Inform }; 81 TelemetryTraceLogger::ProcessingSummaryForIntent ApplySummary{ ConfigurationUnitIntent::Apply }; 82 }; 83 84 size_t GetPriority(ConfigurationUnitResultSource source) 85 { 86 switch (source) 87 { 88 case ConfigurationUnitResultSource::Internal: return 0; 89 case ConfigurationUnitResultSource::UnitProcessing: return 100; 90 case ConfigurationUnitResultSource::SystemState: return 200; 91 case ConfigurationUnitResultSource::ConfigurationSet: return 300; 92 case ConfigurationUnitResultSource::Precondition: return 400; 93 default: return 500; 94 case ConfigurationUnitResultSource::None: return 600; 95 } 96 } 97 98 bool FirstHasPriority(ConfigurationUnitResultSource first, ConfigurationUnitResultSource second) 99 { 100 return GetPriority(first) < GetPriority(second); 101 } 102 103 void ProcessUnitResult(const Configuration::ConfigurationUnit unit, const IConfigurationUnitResultInformation& resultInformation, ConfigRunSummaryData& result) 104 { 105 hresult resultCode = resultInformation.ResultCode(); 106 if (FAILED(resultCode)) 107 { 108 if (result.Result == S_OK || result.Result == resultCode) 109 { 110 result.Result = resultCode; 111 } 112 else 113 { 114 result.Result = WINGET_CONFIG_ERROR_SET_APPLY_FAILED; 115 } 116 } 117 118 ConfigurationUnitResultSource unitFailurePoint = resultInformation.ResultSource(); 119 if (FirstHasPriority(unitFailurePoint, result.FailurePoint)) 120 { 121 result.FailurePoint = unitFailurePoint; 122 } 123 124 TelemetryTraceLogger::ProcessingSummaryForIntent* summaryItem = nullptr; 125 switch (unit.Intent()) 126 { 127 case ConfigurationUnitIntent::Assert: 128 summaryItem = &result.AssertSummary; 129 break; 130 case ConfigurationUnitIntent::Inform: 131 summaryItem = &result.InformSummary; 132 break; 133 case ConfigurationUnitIntent::Apply: 134 case ConfigurationUnitIntent::Unknown: 135 summaryItem = &result.ApplySummary; 136 break; 137 default: 138 return; 139 } 140 141 summaryItem->Count++; 142 143 ConfigurationUnitResultSource resultSource = resultInformation.ResultSource(); 144 if (resultSource != ConfigurationUnitResultSource::Precondition && 145 resultSource != ConfigurationUnitResultSource::ConfigurationSet) 146 { 147 summaryItem->Run++; 148 } 149 150 if (FAILED(resultCode)) 151 { 152 summaryItem->Failed++; 153 } 154 } 155 156 // Runs through a set of results, summarizing them. 157 template <typename Enumerable> 158 ConfigRunSummaryData ProcessRunResult(const Enumerable& results) 159 { 160 ConfigRunSummaryData result; 161 162 for (const auto& item : results) 163 { 164 ProcessUnitResult(item.Unit(), item.ResultInformation(), result); 165 } 166 167 return result; 168 } 169 } 170 171 TelemetryTraceLogger::TelemetryTraceLogger() 172 { 173 std::ignore = CoCreateGuid(&m_activityId); 174 m_version = AppInstaller::Runtime::GetClientVersion(); 175 } 176 177 void TelemetryTraceLogger::SetActivityId(const guid& value) 178 { 179 m_activityId = value; 180 } 181 182 const GUID* TelemetryTraceLogger::GetActivityId() const 183 { 184 return &m_activityId; 185 } 186 187 bool TelemetryTraceLogger::EnableRuntime(bool value) 188 { 189 return m_isRuntimeEnabled.exchange(value); 190 } 191 192 bool TelemetryTraceLogger::IsEnabled() const 193 { 194 return m_isRuntimeEnabled; 195 } 196 197 void TelemetryTraceLogger::SetCaller(std::string_view caller) 198 { 199 m_caller = caller; 200 } 201 202 std::string_view TelemetryTraceLogger::GetCaller() const 203 { 204 return m_caller; 205 } 206 207 void TelemetryTraceLogger::LogConfigUnitRun( 208 const guid& setIdentifier, 209 const guid& unitIdentifier, 210 hstring unitName, 211 hstring moduleName, 212 ConfigurationUnitIntent unitIntent, 213 ConfigurationUnitIntent runIntent, 214 std::string_view action, 215 hresult result, 216 ConfigurationUnitResultSource failurePoint, 217 std::wstring_view settingNames) const noexcept try 218 { 219 // Change unknown to Apply for telemetry, as it will have been treated that way 220 if (unitIntent == ConfigurationUnitIntent::Unknown) 221 { 222 unitIntent = ConfigurationUnitIntent::Apply; 223 } 224 225 if (IsTelemetryEnabled()) 226 { 227 AICLI_TraceLoggingWriteActivity( 228 "ConfigUnitRun", 229 TraceLoggingGuid(setIdentifier, "SetID"), 230 TraceLoggingGuid(unitIdentifier, "UnitID"), 231 AICLI_TraceLoggingWStringView(unitName, "UnitName"), 232 AICLI_TraceLoggingWStringView(moduleName, "ModuleName"), 233 TraceLoggingInt32(static_cast<int32_t>(unitIntent), "UnitIntent"), 234 TraceLoggingInt32(static_cast<int32_t>(runIntent), "RunIntent"), 235 AICLI_TraceLoggingStringView(action, "Action"), 236 TraceLoggingHResult(result, "Result"), 237 TraceLoggingInt32(static_cast<int32_t>(failurePoint), "FailurePoint"), 238 AICLI_TraceLoggingWStringView(settingNames, "SettingsProvided"), 239 TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), 240 TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); 241 242 // Keep in sync with above event! 243 WinGet_WriteEventToDiagnostics( 244 "ConfigUnitRun", 245 WinGet_EventItem(setIdentifier, "SetID"), 246 WinGet_EventItem(unitIdentifier, "UnitID"), 247 WinGet_EventItem(unitName, "UnitName"), 248 WinGet_EventItem(moduleName, "ModuleName"), 249 WinGet_EventItem(static_cast<int32_t>(unitIntent), "UnitIntent"), 250 WinGet_EventItem(static_cast<int32_t>(runIntent), "RunIntent"), 251 WinGet_EventItem(action, "Action"), 252 WinGet_EventItem(result, "Result"), 253 WinGet_EventItem(static_cast<int32_t>(failurePoint), "FailurePoint"), 254 WinGet_EventItem(settingNames, "SettingsProvided")); 255 } 256 } 257 CATCH_LOG(); 258 259 void TelemetryTraceLogger::LogConfigUnitRunIfAppropriate( 260 const guid& setIdentifier, 261 const Configuration::ConfigurationUnit& unit, 262 ConfigurationUnitIntent runIntent, 263 std::string_view action, 264 const IConfigurationUnitResultInformation& resultInformation) const noexcept try 265 { 266 if (!IsTelemetryEnabled()) 267 { 268 return; 269 } 270 271 // We only want to send telemetry for publicly available units. 272 IConfigurationUnitProcessorDetails details = unit.Details(); 273 if (!details || !details.IsPublic()) 274 { 275 return; 276 } 277 278 // Create a single string from the set of top level setting names, ex. "a|b|c". 279 const winrt::Windows::Foundation::Collections::ValueSet& settings = unit.Settings(); 280 std::wostringstream strstr; 281 282 for (const auto& setting : settings) 283 { 284 strstr << static_cast<std::wstring_view>(setting.Key()) << L'|'; 285 } 286 std::wstring allSettingsNames = strstr.str(); 287 if (!allSettingsNames.empty()) 288 { 289 allSettingsNames.pop_back(); 290 } 291 292 LogConfigUnitRun(setIdentifier, unit.InstanceIdentifier(), unit.Type(), details.ModuleName(), unit.Intent(), runIntent, action, resultInformation.ResultCode(), resultInformation.ResultSource(), allSettingsNames); 293 } 294 CATCH_LOG(); 295 296 void TelemetryTraceLogger::LogConfigProcessingSummary( 297 const guid& setIdentifier, 298 std::string_view inputHash, 299 ConfigurationUnitIntent runIntent, 300 hresult result, 301 ConfigurationUnitResultSource failurePoint, 302 const ProcessingSummaryForIntent& assertSummary, 303 const ProcessingSummaryForIntent& informSummary, 304 const ProcessingSummaryForIntent& applySummary) const noexcept try 305 { 306 if (IsTelemetryEnabled()) 307 { 308 AICLI_TraceLoggingWriteActivity( 309 "ConfigProcessingSummary", 310 TraceLoggingGuid(setIdentifier, "SetID"), 311 AICLI_TraceLoggingStringView(inputHash, "InputHash"), 312 TraceLoggingBool(false, "FromHistory"), // deprecated 313 TraceLoggingInt32(static_cast<int32_t>(runIntent), "RunIntent"), 314 TraceLoggingHResult(result, "Result"), 315 TraceLoggingInt32(static_cast<int32_t>(failurePoint), "FailurePoint"), 316 AICLI_TraceLoggingProcessingSummaryForIntent(assertSummary, "Assert", "Asserts"), 317 AICLI_TraceLoggingProcessingSummaryForIntent(informSummary, "Inform", "Informs"), 318 AICLI_TraceLoggingProcessingSummaryForIntent(applySummary, "Apply", "Applies"), 319 TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), 320 TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); 321 322 // Keep in sync with above event! 323 WinGet_WriteEventToDiagnostics( 324 "ConfigProcessingSummary", 325 WinGet_EventItem(setIdentifier, "SetID"), 326 WinGet_EventItem(inputHash, "InputHash"), 327 WinGet_EventItem(false, "FromHistory"), // deprecated 328 WinGet_EventItem(static_cast<int32_t>(runIntent), "RunIntent"), 329 WinGet_EventItem(result, "Result"), 330 WinGet_EventItem(static_cast<int32_t>(failurePoint), "FailurePoint"), 331 WinGet_SummaryForIntentItem(assertSummary, "Assert", "Asserts"), 332 WinGet_SummaryForIntentItem(informSummary, "Inform", "Informs"), 333 WinGet_SummaryForIntentItem(applySummary, "Apply", "Applies")); 334 } 335 } 336 CATCH_LOG(); 337 338 void TelemetryTraceLogger::LogConfigProcessingSummaryForTest( 339 const ConfigurationSet& configurationSet, 340 const TestConfigurationSetResult& result) const noexcept try 341 { 342 if (!IsTelemetryEnabled()) 343 { 344 return; 345 } 346 347 ConfigRunSummaryData summaryData = ProcessRunResult(result.UnitResults()); 348 349 LogConfigProcessingSummary(configurationSet.InstanceIdentifier(), configurationSet.GetInputHash(), ConfigurationUnitIntent::Assert, 350 summaryData.Result, summaryData.FailurePoint, summaryData.AssertSummary, summaryData.InformSummary, summaryData.ApplySummary); 351 } 352 CATCH_LOG(); 353 354 void TelemetryTraceLogger::LogConfigProcessingSummaryForTestException( 355 const ConfigurationSet& configurationSet, 356 hresult error, 357 const TestConfigurationSetResult& result) const noexcept try 358 { 359 if (!IsTelemetryEnabled()) 360 { 361 return; 362 } 363 364 ConfigRunSummaryData summaryData = ProcessRunResult(result.UnitResults()); 365 366 LogConfigProcessingSummary(configurationSet.InstanceIdentifier(), configurationSet.GetInputHash(), ConfigurationUnitIntent::Assert, 367 error, ConfigurationUnitResultSource::Internal, summaryData.AssertSummary, summaryData.InformSummary, summaryData.ApplySummary); 368 } 369 CATCH_LOG(); 370 371 void TelemetryTraceLogger::LogConfigProcessingSummaryForApply( 372 const ConfigurationSet& configurationSet, 373 const ApplyConfigurationSetResult& result) const noexcept try 374 { 375 if (!IsTelemetryEnabled()) 376 { 377 return; 378 } 379 380 ConfigRunSummaryData summaryData = ProcessRunResult(result.UnitResults()); 381 382 LogConfigProcessingSummary(configurationSet.InstanceIdentifier(), configurationSet.GetInputHash(), ConfigurationUnitIntent::Apply, 383 result.ResultCode(), summaryData.FailurePoint, summaryData.AssertSummary, summaryData.InformSummary, summaryData.ApplySummary); 384 } 385 CATCH_LOG(); 386 387 void TelemetryTraceLogger::LogConfigProcessingSummaryForApplyException( 388 const ConfigurationSet& configurationSet, 389 hresult error, 390 const ApplyConfigurationSetResult& result) const noexcept try 391 { 392 if (!IsTelemetryEnabled()) 393 { 394 return; 395 } 396 397 ConfigRunSummaryData summaryData = ProcessRunResult(result.UnitResults()); 398 399 LogConfigProcessingSummary(configurationSet.InstanceIdentifier(), configurationSet.GetInputHash(), ConfigurationUnitIntent::Apply, 400 error, ConfigurationUnitResultSource::Internal, summaryData.AssertSummary, summaryData.InformSummary, summaryData.ApplySummary); 401 } 402 CATCH_LOG(); 403 404 bool TelemetryTraceLogger::IsTelemetryEnabled() const noexcept 405 { 406 #ifdef AICLI_DISABLE_TEST_HOOKS 407 return g_IsTelemetryProviderEnabled && m_isRuntimeEnabled; 408 #else 409 // For testing, only use the local enable state. 410 return m_isRuntimeEnabled; 411 #endif 412 } 413 }