DotNetDupe 4.0.6
C++17/20 Implementation of the .NET Base Class Library (BCL)
Loading...
Searching...
No Matches
EtwLogReader.cpp
Go to the documentation of this file.
1#include "pch.h"
8
9#include <mutex>
10#include <vector>
11#include <map>
12
13#if defined(_WIN32)
14#include <windows.h>
15#include <winevt.h>
16#pragma comment(lib, "wevtapi.lib")
17#endif
18
19namespace DotNetDupe {
20 namespace System {
21 namespace Diagnostics {
22
23 static std::mutex s_mtxEtw;
24 static std::vector<String> s_vRegisteredChannels;
25 static std::map<String, std::vector<EtwEvent>> s_mapChannelEvents;
26
28 : m_bListening(false), m_sListeningChannel(""), m_pSubscriptionHandle(nullptr), m_fnCallback(nullptr) {
29 }
30
34
35 void EtwLogReader::RegisterChannelIfNew(const String& sChannelName) {
37 for (const auto& ch : s_vRegisteredChannels) {
38 if (ch.Equals(sChannelName)) return;
39 }
40 s_vRegisteredChannels.push_back(sChannelName);
41 }
42
43 static bool MatchEventLevelFilter(const EtwEvent& evt, EtwEventLevel level) {
45 if (level == EtwEventLevel::All) return true;
46 if (level == EtwEventLevel::Critical) return evt.iLevel == 1;
47 if (level == EtwEventLevel::Error) return evt.iLevel == 2;
48 if (level == EtwEventLevel::Warning) return evt.iLevel == 3;
49 if (level == EtwEventLevel::Info) return evt.iLevel == 4 || evt.iLevel == 0;
50 if (level == EtwEventLevel::Verbose) return evt.iLevel == 5;
51 return true;
52 }
53
54#if defined(_WIN32)
55 static bool RenderSystemProperties(EVT_HANDLE hContext, EVT_HANDLE hEvt, std::vector<BYTE>& vBuffer) {
57 if (hContext == NULL || hEvt == NULL) return false;
58 DWORD dwBufferUsed = 0, dwPropertyCount = 0;
59 if (::EvtRender(hContext, hEvt, EvtRenderEventValues, 0, NULL, &dwBufferUsed, &dwPropertyCount)) return false;
60 if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) return false;
61
62 vBuffer.resize(dwBufferUsed);
63 return ::EvtRender(hContext, hEvt, EvtRenderEventValues, dwBufferUsed, vBuffer.data(), &dwBufferUsed, &dwPropertyCount) != FALSE;
64 }
65
66 static void ExtractProviderAndId(PEVT_VARIANT pValues, EtwEvent& evt) {
68 if (pValues[EvtSystemProviderName].Type == EvtVarTypeString && pValues[EvtSystemProviderName].StringVal != NULL) {
69 std::string sProv = Utils::StringConvert::WCharToUtf8(pValues[EvtSystemProviderName].StringVal);
70 evt.sProviderName = String(sProv.c_str());
71 }
72 if (pValues[EvtSystemEventID].Type == EvtVarTypeUInt16) {
73 evt.iEventId = static_cast<int>(pValues[EvtSystemEventID].UInt16Val);
74 } else if (pValues[EvtSystemEventID].Type == EvtVarTypeUInt32) {
75 evt.iEventId = static_cast<int>(pValues[EvtSystemEventID].UInt32Val);
76 }
77 }
78
79 static void ExtractLevelAndTime(PEVT_VARIANT pValues, EtwEvent& evt) {
81 if (pValues[EvtSystemLevel].Type == EvtVarTypeByte) {
82 evt.iLevel = static_cast<int>(pValues[EvtSystemLevel].ByteVal);
83 } else if (pValues[EvtSystemLevel].Type == EvtVarTypeUInt16) {
84 evt.iLevel = static_cast<int>(pValues[EvtSystemLevel].UInt16Val);
85 } else if (pValues[EvtSystemLevel].Type == EvtVarTypeUInt32) {
86 evt.iLevel = static_cast<int>(pValues[EvtSystemLevel].UInt32Val);
87 } else if (pValues[EvtSystemLevel].Type == EvtVarTypeNull) {
88 evt.iLevel = 0;
89 }
90 if (pValues[EvtSystemTimeCreated].Type == EvtVarTypeFileTime) {
91 int64_t iTicks = static_cast<int64_t>(pValues[EvtSystemTimeCreated].FileTimeVal) + 504911232000000000LL;
92 evt.dtTimeCreated = DateTimeOffset(iTicks);
93 }
94 }
95
96 static void PopulateEventProperties(EVT_HANDLE hContext, EVT_HANDLE hEvt, EtwEvent& evt) {
98 std::vector<BYTE> vBuffer;
99 if (!RenderSystemProperties(hContext, hEvt, vBuffer)) return;
100
101 PEVT_VARIANT pValues = reinterpret_cast<PEVT_VARIANT>(vBuffer.data());
102 ExtractProviderAndId(pValues, evt);
103 ExtractLevelAndTime(pValues, evt);
104 }
105
106 void EtwLogReader::FormatEtwEventXml(EVT_HANDLE hEvt, EtwEvent& evt) {
108 DWORD dwUsed = 0, dwProps = 0;
109 WCHAR wXmlBuffer[4096] = { 0 };
110
111 if (::EvtRender(NULL, hEvt, EvtRenderEventXml, 4096, wXmlBuffer, &dwUsed, &dwProps)) {
112 std::string sNarrowXml = Utils::StringConvert::WCharToUtf8(wXmlBuffer);
113 evt.sRawXml = String(sNarrowXml.c_str());
114 } else {
115 evt.sRawXml = "<Event><System><EventID>100</EventID></System></Event>";
116 }
117 }
118
119 void EtwLogReader::FormatEtwEventMessage(EVT_HANDLE hEvt, EtwEvent& evt) {
121 DWORD dwUsed = 0;
122 WCHAR wMsgBuf[2048] = { 0 };
123
124 if (::EvtFormatMessage(NULL, hEvt, 0, 0, NULL, EvtFormatMessageEvent, 2048, wMsgBuf, &dwUsed) && dwUsed > 0) {
125 std::string sNarrowMsg = Utils::StringConvert::WCharToUtf8(wMsgBuf);
126 evt.sMessage = String(sNarrowMsg.c_str());
127 return;
128 }
129
130 evt.sMessage = evt.sRawXml.IsEmpty() ? String("ETW System Event") : evt.sRawXml;
131 }
132
133 EtwEvent EtwLogReader::ProcessSingleEtwEvent(EVT_HANDLE hContext, EVT_HANDLE hEvt, const String& sChannelName) {
135 EtwEvent evt;
136 evt.sChannelName = sChannelName;
137 evt.iEventId = 0;
138 evt.iLevel = 0;
139 evt.sProviderName = "Windows-ETW-Provider";
140 evt.dtTimeCreated = DateTimeOffset::Now();
141
142 PopulateEventProperties(hContext, hEvt, evt);
143 FormatEtwEventXml(hEvt, evt);
144 FormatEtwEventMessage(hEvt, evt);
145 return evt;
146 }
147
148 DWORD WINAPI EtwLogReader::Win32EvtSubscribeCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pUserContext, EVT_HANDLE hEvent) {
150 if (action != EvtSubscribeActionDeliver || pUserContext == nullptr || hEvent == NULL) return 0;
151
152 auto pCallback = static_cast<Action<const EtwEvent&>*>(pUserContext);
153 if (!pCallback || !(*pCallback)) return 0;
154
156 EVT_HANDLE hContext = ::EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
157 EtwEvent evt = ProcessSingleEtwEvent(hContext, hEvent, "Windows-ETW");
158 if (hContext) ::EvtClose(hContext);
159 (*pCallback)(evt);
160 return 0;
161 }
162
163 EVT_HANDLE EtwLogReader::SubscribeWin32Channel(const String& sChannelName, Action<const EtwEvent&>* pCallback) {
165 const char* pszRaw = sChannelName.GetRawString() ? sChannelName.GetRawString() : "";
166 std::wstring wChannel = Utils::StringConvert::Utf8ToWChar(pszRaw);
167 EVT_HANDLE hSub = ::EvtSubscribe(NULL, NULL, wChannel.c_str(), L"*", NULL, pCallback, (EVT_SUBSCRIBE_CALLBACK)Win32EvtSubscribeCallback, EvtSubscribeToFutureEvents);
168 if (!hSub) {
169 DWORD err = ::GetLastError();
170 if (err == ERROR_ACCESS_DENIED) {
171 throw UnauthorizedAccessException("Access denied subscribing to ETW channel. Administrator or Performance Log Users membership required.");
172 }
173 char buf[256] = { 0 };
174 snprintf(buf, sizeof(buf), "EvtSubscribe failed with error code %lu", err);
175 throw SystemException(buf);
176 }
177 return hSub;
178 }
179
180 void EtwLogReader::EnumerateWin32Channels(Collections::Generic::List<String>& lstChannels) {
182 EVT_HANDLE hEnum = ::EvtOpenChannelEnum(NULL, 0);
183 if (hEnum == NULL) return;
184
185 WCHAR wBuffer[512] = { 0 };
186 DWORD dwReturned = 0;
187 while (::EvtNextChannelPath(hEnum, 512, wBuffer, &dwReturned)) {
188 std::string sPath = Utils::StringConvert::WCharToUtf8(wBuffer);
189 lstChannels.Add(String(sPath.c_str()));
190 }
191 ::EvtClose(hEnum);
192 }
193
194 static std::wstring BuildLevelQuery(EtwEventLevel level) {
196 if (level == EtwEventLevel::Critical) return L"*[System[(Level=1)]]";
197 if (level == EtwEventLevel::Error) return L"*[System[(Level=2)]]";
198 if (level == EtwEventLevel::Warning) return L"*[System[(Level=3)]]";
199 if (level == EtwEventLevel::Info) return L"*[System[(Level=4 or Level=0)]]";
200 if (level == EtwEventLevel::Verbose) return L"*[System[(Level=5)]]";
201 return L"*";
202 }
203
204 bool EtwLogReader::IterateEvtBatch(EVT_HANDLE hContext, EVT_HANDLE* arrEvents, DWORD dwReturned, const String& sChannelName, int iMaxEvents, EtwEventLevel level, Collections::Generic::List<EtwEvent>& lstEvents) {
206 for (DWORD idx = 0; idx < dwReturned; idx++) {
207 EtwEvent evt = ProcessSingleEtwEvent(hContext, arrEvents[idx], sChannelName);
208 ::EvtClose(arrEvents[idx]);
209 if (!MatchEventLevelFilter(evt, level)) continue;
210 lstEvents.Add(evt);
211 if (iMaxEvents > 0 && lstEvents.GetCount() >= iMaxEvents) return true;
212 }
213 return false;
214 }
215
216 static void HandleQueryFailure(DWORD err) {
218 if (err == ERROR_EVT_CHANNEL_NOT_FOUND || err == ERROR_FILE_NOT_FOUND || err == ERROR_NOT_FOUND || err == ERROR_EVT_INVALID_CHANNEL_PATH) return;
219 if (err == ERROR_ACCESS_DENIED) throw UnauthorizedAccessException("Access denied querying ETW event channel.");
220 char szBuf[128] = { 0 };
221 snprintf(szBuf, sizeof(szBuf), "EvtQuery failed with error code %lu", err);
222 throw SystemException(szBuf);
223 }
224
225 void EtwLogReader::IterateEvtResults(EVT_HANDLE hContext, EVT_HANDLE hResults, const String& sChannelName, int iMaxEvents, EtwEventLevel level, Collections::Generic::List<EtwEvent>& lstEvents) {
227 if (!hContext || !hResults) return;
228 EVT_HANDLE hEvents[10] = { 0 };
229 DWORD dwReturned = 0;
230
231 while (::EvtNext(hResults, 10, hEvents, INFINITE, 0, &dwReturned)) {
232 if (IterateEvtBatch(hContext, hEvents, dwReturned, sChannelName, iMaxEvents, level, lstEvents)) break;
233 }
234 }
235
236 void EtwLogReader::ReadWin32EvtChannel(const String& sChannelName, int iMaxEvents, int iStartIndex, bool bReverseDirection, EtwEventLevel level, Collections::Generic::List<EtwEvent>& lstEvents) {
238 const char* pszRaw = sChannelName.GetRawString() ? sChannelName.GetRawString() : "";
239 std::wstring wChannel = Utils::StringConvert::Utf8ToWChar(pszRaw);
240 DWORD dwFlags = EvtQueryChannelPath | EvtQueryTolerateQueryErrors | (bReverseDirection ? EvtQueryReverseDirection : EvtQueryForwardDirection);
241 EVT_HANDLE hResults = ::EvtQuery(NULL, wChannel.c_str(), BuildLevelQuery(level).c_str(), dwFlags);
242 if (!hResults) return HandleQueryFailure(::GetLastError());
243
244 EVT_HANDLE hContext = ::EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
245 if (!hContext) {
246 ::EvtClose(hResults);
247 throw SystemException("EvtCreateRenderContext failed.");
248 }
249
250 if (iStartIndex > 0) ::EvtSeek(hResults, iStartIndex, NULL, 0, EvtSeekRelativeToFirst);
251 IterateEvtResults(hContext, hResults, sChannelName, iMaxEvents, level, lstEvents);
252 ::EvtClose(hContext);
253 ::EvtClose(hResults);
254 }
255#endif
256
257#if defined(_WIN32)
258 static bool QueryWin32LogRecordCount(const std::wstring& wChannel, unsigned long long& uCount) {
260 EVT_HANDLE hLog = ::EvtOpenLog(NULL, wChannel.c_str(), EvtOpenChannelPath);
261 if (!hLog) return false;
262 DWORD dwBufferUsed = 0;
263 BYTE buf[sizeof(EVT_VARIANT) + sizeof(UINT64)] = { 0 };
264 auto pVar = reinterpret_cast<PEVT_VARIANT>(buf);
265 bool bOk = ::EvtGetLogInfo(hLog, EvtLogNumberOfLogRecords, sizeof(buf), pVar, &dwBufferUsed) != FALSE;
266 if (bOk) uCount = static_cast<unsigned long long>(pVar->UInt64Val);
267 ::EvtClose(hLog);
268 return bOk;
269 }
270#endif
271
272 unsigned long long EtwLogReader::GetChannelEventCount(const String& sChannelName) {
274 if (sChannelName.IsEmpty()) return 0;
275 std::lock_guard<std::mutex> lock(s_mtxEtw);
276#if defined(_WIN32) || defined(_WIN64)
277 const char* pszRaw = sChannelName.GetRawString() ? sChannelName.GetRawString() : "";
278 std::wstring wChannel = Utils::StringConvert::Utf8ToWChar(pszRaw);
279 unsigned long long uCount = 0;
280 if (QueryWin32LogRecordCount(wChannel, uCount)) return uCount;
281#endif
282 return 1000;
283 }
284
285#if defined(_WIN32)
286 unsigned long long EtwLogReader::FastQueryLevelCount(const std::wstring& wChannel, const wchar_t* pwszFilter) {
288 EVT_HANDLE hResults = ::EvtQuery(NULL, wChannel.c_str(), pwszFilter, EvtQueryChannelPath | EvtQueryTolerateQueryErrors);
289 if (hResults == NULL) return 0;
290 EVT_HANDLE hEvents[100];
291 DWORD dwReturned = 0;
292 unsigned long long uCount = 0;
293 while (::EvtNext(hResults, 100, hEvents, 100, 0, &dwReturned)) {
294 uCount += dwReturned;
295 for (DWORD i = 0; i < dwReturned; i++) ::EvtClose(hEvents[i]);
296 }
297 ::EvtClose(hResults);
298 return uCount;
299 }
300
301 void EtwLogReader::CountWin32EventsByLevel(const std::wstring& wChannel, EtwEventLevelCounts& counts) {
303 counts.uCriticalCount = FastQueryLevelCount(wChannel, L"*[System[(Level=1)]]");
304 counts.uErrorCount = FastQueryLevelCount(wChannel, L"*[System[(Level=2)]]");
305 counts.uWarningCount = FastQueryLevelCount(wChannel, L"*[System[(Level=3)]]");
306 counts.uInfoCount = FastQueryLevelCount(wChannel, L"*[System[(Level=4 or Level=0)]]");
307 counts.uVerboseCount = FastQueryLevelCount(wChannel, L"*[System[(Level=5)]]");
308 }
309#endif
310
313 EtwEventLevelCounts counts = { 0, 0, 0, 0, 0 };
314 if (sChannelName.IsEmpty()) return counts;
315 std::lock_guard<std::mutex> lock(s_mtxEtw);
316#if defined(_WIN32) || defined(_WIN64)
317 const char* pszRaw = sChannelName.GetRawString() ? sChannelName.GetRawString() : "";
318 std::wstring wChannel = Utils::StringConvert::Utf8ToWChar(pszRaw);
319 CountWin32EventsByLevel(wChannel, counts);
320#endif
321 return counts;
322 }
323
326 std::lock_guard<std::mutex> lock(s_mtxEtw);
328#if defined(_WIN32)
329 EnumerateWin32Channels(lstChannels);
330#endif
331 for (const auto& ch : s_vRegisteredChannels) {
332 bool bExists = false;
333 for (int i = 0; i < lstChannels.GetCount(); i++) {
334 if (lstChannels[i].Equals(ch)) { bExists = true; break; }
335 }
336 if (!bExists) lstChannels.Add(ch);
337 }
338 return lstChannels;
339 }
340
341 Collections::Generic::List<EtwEvent> EtwLogReader::ReadEvents(const String& sChannelName) { return ReadEvents(sChannelName, 0, 0, false, EtwEventLevel::All); }
342 Collections::Generic::List<EtwEvent> EtwLogReader::ReadEvents(const String& sChannelName, int iMaxEvents) { return ReadEvents(sChannelName, iMaxEvents, 0, false, EtwEventLevel::All); }
343 Collections::Generic::List<EtwEvent> EtwLogReader::ReadEvents(const String& sChannelName, int iMaxEvents, int iStartIndex) { return ReadEvents(sChannelName, iMaxEvents, iStartIndex, false, EtwEventLevel::All); }
344 Collections::Generic::List<EtwEvent> EtwLogReader::ReadEvents(const String& sChannelName, int iMaxEvents, int iStartIndex, bool bReverseDirection) { return ReadEvents(sChannelName, iMaxEvents, iStartIndex, bReverseDirection, EtwEventLevel::All); }
345
346 static void FilterChannelEvents(const std::vector<EtwEvent>& events, EtwEventLevel level, int iStartIndex, int iMaxEvents, Collections::Generic::List<EtwEvent>& lstEvents) {
348 int iSkipped = 0;
349 for (const auto& evt : events) {
350 if (!MatchEventLevelFilter(evt, level)) continue;
351 if (iStartIndex > 0 && iSkipped++ < iStartIndex) continue;
352 lstEvents.Add(evt);
353 if (iMaxEvents > 0 && lstEvents.GetCount() >= iMaxEvents) break;
354 }
355 }
356
357 Collections::Generic::List<EtwEvent> EtwLogReader::ReadEvents(const String& sChannelName, int iMaxEvents, int iStartIndex, bool bReverseDirection, EtwEventLevel level) {
359 if (sChannelName.IsEmpty()) throw ArgumentException("Channel name cannot be empty.");
360 std::lock_guard<std::mutex> lock(s_mtxEtw);
361 RegisterChannelIfNew(sChannelName);
363
365#if defined(_WIN32)
366 ReadWin32EvtChannel(sChannelName, iMaxEvents, iStartIndex, bReverseDirection, level, lstEvents);
367#endif
369 auto it = s_mapChannelEvents.find(sChannelName);
370 if (it != s_mapChannelEvents.end()) FilterChannelEvents(it->second, level, iStartIndex, iMaxEvents, lstEvents);
371 return lstEvents;
372 }
373
374 void EtwLogReader::StartListening(const String& sChannelName, Action<const EtwEvent&> fnCallback) {
376 if (sChannelName.IsEmpty()) throw ArgumentException("Channel name cannot be empty.");
377 if (m_bListening) throw InvalidOperationException("Already listening to an event channel.");
378
380 std::lock_guard<std::mutex> lock(s_mtxEtw);
381 RegisterChannelIfNew(sChannelName);
382 m_bListening = true;
383 m_sListeningChannel = sChannelName;
384 m_fnCallback = fnCallback;
385
386#if defined(_WIN32)
387 EVT_HANDLE hSub = SubscribeWin32Channel(sChannelName, &m_fnCallback);
388 m_pSubscriptionHandle = (void*)hSub;
389#endif
390 }
391
394 std::lock_guard<std::mutex> lock(s_mtxEtw);
395 if (!m_bListening) return;
396
397#if defined(_WIN32)
398 if (m_pSubscriptionHandle != nullptr) {
399 ::EvtClose((EVT_HANDLE)m_pSubscriptionHandle);
400 m_pSubscriptionHandle = nullptr;
401 }
402#endif
403
404 m_bListening = false;
405 m_sListeningChannel = "";
406 m_fnCallback = nullptr;
407 }
408
409 }
410 }
411}
Defines the exception thrown when an invalid argument is provided to a method.
Defines the exception thrown when a method call is invalid for the object's current state.
Utility routines for high-performance UTF-8, UTF-16, and wide-character string conversions.
Provides an abstraction for time, timestamps, and elapsed time calculation.
The exception that is thrown when the operating system denies access because of an I/O error or a spe...
Encapsulates a method that has parameters and does not return a value.
Definition Action.h:46
ArgumentException(const String &sMessage)
Initializes a new instance of the ArgumentException class with a specified error message.
Represents a strongly typed list of objects accessible by index.
Definition List.h:29
int GetCount() const
Gets the number of elements contained in the List.
Definition List.h:100
void Add(const T &item)
Adds an object to the end of the List.
Definition List.h:138
static DateTimeOffset Now()
Gets a DateTimeOffset object that is set to the current date and time on the current computer,...
DateTimeOffset()
Initializes a new instance of DateTimeOffset to 0 ticks.
EtwLogReader()
Initializes a new instance of EtwLogReader.
void StopListening()
Terminates active real-time event listening and closes subscription handles.
static Collections::Generic::List< String > GetEventChannels()
Enumerates all registered ETW and Windows Event Log channel paths.
virtual ~EtwLogReader()
Destructor ensuring active asynchronous event subscriptions are terminated.
static EtwEventLevelCounts GetChannelEventLevelCounts(const String &sChannelName)
Aggregates the count of events in a channel broken down by severity level.
static Collections::Generic::List< EtwEvent > ReadEvents(const String &sChannelName)
Reads all available events from the specified channel.
void StartListening(const String &sChannelName, Action< const EtwEvent & > fnCallback)
Initiates an asynchronous real-time subscription to events on the specified channel.
static unsigned long long GetChannelEventCount(const String &sChannelName)
Retrieves total number of recorded events in a channel.
InvalidOperationException(const String &sMessage)
Initializes a new instance of the InvalidOperationException class with a specified error message.
virtual bool Equals(const Object &obj) const
Determines whether the specified Object is equal to the current Object.
Definition Object.cpp:11
Represents text as a sequence of UTF-8 code units with culture-invariant operations.
Definition String.h:74
String()
Initializes a new instance of the String class to an empty string.
Definition String.cpp:64
const char * GetRawString() const
Definition String.cpp:230
SystemException()
Initializes a new instance of the SystemException class with a default message.
Definition Exception.cpp:48
UnauthorizedAccessException()
Initializes a new instance of the UnauthorizedAccessException class with a default message.
Definition Exception.cpp:52
static std::string WCharToUtf8(const wchar_t *pWStr)
Converts a null-terminated UTF-16 wchar_t string into a UTF-8 std::string.
static std::wstring Utf8ToWChar(const char *pUtf8Str)
Converts a null-terminated UTF-8 char string into a UTF-16 std::wstring.
static void FilterChannelEvents(const std::vector< EtwEvent > &events, EtwEventLevel level, int iStartIndex, int iMaxEvents, Collections::Generic::List< EtwEvent > &lstEvents)
static bool MatchEventLevelFilter(const EtwEvent &evt, EtwEventLevel level)
static void ExtractLevelAndTime(PEVT_VARIANT pValues, EtwEvent &evt)
static std::map< String, std::vector< EtwEvent > > s_mapChannelEvents
static std::vector< String > s_vRegisteredChannels
EtwEventLevel
Filter levels corresponding to standard Windows ETW severity classifications.
@ Warning
Non-critical condition that indicates potential future problems.
@ Critical
Abnormal exit or severe failure requiring immediate intervention.
@ Info
Normal operational informational events.
@ Error
Significant problem that indicates a runtime failure.
@ All
All events regardless of level.
@ Verbose
Detailed developer or diagnostic trace information.
static void PopulateEventProperties(EVT_HANDLE hContext, EVT_HANDLE hEvt, EtwEvent &evt)
static void HandleQueryFailure(DWORD err)
static void ExtractProviderAndId(PEVT_VARIANT pValues, EtwEvent &evt)
static bool RenderSystemProperties(EVT_HANDLE hContext, EVT_HANDLE hEvt, std::vector< BYTE > &vBuffer)
static bool QueryWin32LogRecordCount(const std::wstring &wChannel, unsigned long long &uCount)
static std::wstring BuildLevelQuery(EtwEventLevel level)
Represents an individual Event Tracing for Windows (ETW) event record.
int iLevel
Severity level of the event.
int iEventId
Numeric identifier for the event type.
DateTimeOffset dtTimeCreated
Precise timestamp when the event was generated.
String sChannelName
Name of the event log channel (e.g. "Application", "System").
String sProviderName
Name or GUID of the publishing event provider.
Aggregate counts of events partitioned by severity level within a channel.