Преобразование Python win32evtlog объектов в XML
У меня есть приложение, которое использует win32evtlog для получения и отображения различных событий, и я хотел бы ограничить отображение событиями определенного уровня, но win32evtlog не возвращает это. Кажется, что вы можете преобразовать событие в XML и затем извлечь эту информацию, но я не могу понять, как вы переводите событие из цикла в XML.
Я могу перейти к следующему и использовать его для отображения данных, которые есть у LogObject, например, LogObject.TimeGenerated.
Log = win32evtlog.OpenEventLog('localhost', 'Application')
while 1:
LogObjects = winev32tlog.ReadEventLog(Log, win32evtlog.EVENTLOG_BACKWARDS_READ|wine32vtlog.EVENTLOG_SEQUENTIAL_READ, 0)
if not LogObjects:
break
for LogObject in LogObjects:
Я пробовал конвертировать используя
LogObjectXML = win32evtlog.EvtRender(LogObject, 1)
Это к сожалению возвращается
TypeError: The object is not a PyHANDLE object
Так что я знаю, что мне нужно получить некоторый объект-дескриптор, который я могу использовать, чтобы указать EvtRender на правильное событие, но не могу понять, как я это делаю.
Этот вопрос очень похож на Как получить из Python win32evtlog остальной информации? но решение там не ответило на критический момент того, как мы конвертируем объект в XML.
- == Отредактировано с информацией о XML для CristiFati ==-
Ниже приведен пример события приложения, в котором сообщение о событии гласит:
Updated Windows Defender status successfully to SECURITY_PRODUCT_STATE_ON.
XML для просмотра событий, как показано ниже
- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
<Provider Name="SecurityCenter" />
<EventID Qualifiers="0">15</EventID>
<Level>4</Level>
<Task>0</Task>
<Keywords>0x80000000000000</Keywords>
<TimeCreated SystemTime="2017-05-23T07:36:27.627108000Z" />
<EventRecordID>49419</EventRecordID>
<Channel>Application</Channel>
<Computer>Name.domain.here</Computer>
<Security />
</System>
- <EventData>
<Data>Windows Defender</Data>
<Data>SECURITY_PRODUCT_STATE_ON</Data>
</EventData>
</Event>
1 ответ
ReadEventLog
возвращается PyEventLogRecord
s (оболочка над [MSDN]: EVENTLOGRECORD
структура), в то время как EvtRender
ожидает (вам нужно работать с) PyHANDLE
с (PyEVT_HANDLE
s (оболочка над [MSDN]: EVT_HANDLE
) чтобы быть более точным).
Итак, для получения данных XML вам нужно использовать семейство функций, которое работает с этим типом: например, EvtQuery
, EvtNext
:
import pywintypes
import win32evtlog
INFINITE = 0xFFFFFFFF
EVTLOG_READ_BUF_LEN_MAX = 0x7FFFF
def get_record_data(eventlog_record):
ret = dict()
for key in dir(eventlog_record):
if 'A' < key[0] < 'Z':
ret[key] = getattr(eventlog_record, key)
return ret
def get_eventlogs(source_name="Application", buf_size=EVTLOG_READ_BUF_LEN_MAX, backwards=True):
ret = list()
evt_log = win32evtlog.OpenEventLog(None, source_name)
read_flags = win32evtlog.EVENTLOG_SEQUENTIAL_READ
if backwards:
read_flags |= win32evtlog.EVENTLOG_BACKWARDS_READ
else:
read_flags |= win32evtlog.EVENTLOG_FORWARDS_READ
offset = 0
eventlog_records = win32evtlog.ReadEventLog(evt_log, read_flags, offset, buf_size)
while eventlog_records:
ret.extend(eventlog_records)
offset += len(eventlog_records)
eventlog_records = win32evtlog.ReadEventLog(evt_log, read_flags, offset, buf_size)
win32evtlog.CloseEventLog(evt_log)
return ret
def get_events_xmls(channel_name="Application", events_batch_num=100, backwards=True):
ret = list()
flags = win32evtlog.EvtQueryChannelPath
if backwards:
flags |= win32evtlog.EvtQueryReverseDirection
try:
query_results = win32evtlog.EvtQuery(channel_name, flags, None, None)
except pywintypes.error as e:
print(e)
return ret
events = win32evtlog.EvtNext(query_results, events_batch_num, INFINITE, 0)
while events:
for event in events:
ret.append(win32evtlog.EvtRender(event, win32evtlog.EvtRenderEventXml))
events = win32evtlog.EvtNext(query_results, events_batch_num, INFINITE, 0)
return ret
def main():
import sys, os
from collections import OrderedDict
standard_log_names = ["Application", "System", "Security"]
source_channel_dict = OrderedDict()
for item in standard_log_names:
source_channel_dict[item] = item
for item in ["Windows Powershell"]: # !!! This works on my machine (96 events)
source_channel_dict[item] = item
for source, channel in source_channel_dict.items():
print(source, channel)
logs = get_eventlogs(source_name=source)
xmls = get_events_xmls(channel_name=channel)
#print("\n", get_record_data(logs[0]))
#print(xmls[0])
#print("\n", get_record_data(logs[-1]))
#print(xmls[-1])
print(len(logs))
print(len(xmls))
if __name__ == "__main__":
main()
Примечания:
- 2 списка должны иметь одинаковую длину. N- я запись в каждом из них должна ссылаться на одно и то же событие (при условии, что обе функции вызываются с одинаковым значением для
backwards
аргумент (читайте ниже) get_events_xmls
:- Возвращает список блобов XML, связанных с событиями
- Обработка ошибок не самая лучшая, вы можете обернуть все вызовы API в
try/except
пункты (я не сталкивался с ошибками, поэтому я не уверен, в каких ситуациях может возникнуть исключение) - Вы можете немного поиграть с [MSDN]:
EvtNext
аргументы функции (Timeout
а такжеEventArraySize
для тонкой настройки производительности; для меня ~20 тыс. событий были обработаны в течение <10 секунд - из которых печать текста и преобразования заняли больше всего) - В Py3 XML представляют собой [Python]: байты, а не обычные строки (мне пришлось их кодировать, потому что некоторые из них содержат символы, не входящие в ASCII, и попытка их напечатать приведет к
UnicodeEncodeError
) - Возможна фильтрация событий, проверьте [MSDN]:
EvtQuery
Аргументы функции (Flags
а такжеQuery
) - Обратите внимание
backwards
аргумент, который позволяет проходить события в обратном (хронологическом) порядке (по умолчанию установленоTrue
).
get_record_data
:- Это просто удобная функция, она преобразует
PyEventLogRecord
объект в словарь Python - Преобразование основано на том факте, что поля, которые нам нужны, начинаются с заглавной буквы (
EventID
,ComputerName
,TimeGenerated
...), поэтому его нельзя использовать в производстве - Это не преобразует фактические значения (
TimeGenerated
значениеpywintypes.datetime(2017, 3, 11, 3, 46, 47)
)
- Это просто удобная функция, она преобразует
get_eventlogs
:- Возвращает список
PyEventLogRecord
s - Как в
get_events_xmls
Обратите внимание наbackwards
аргумент - Я должен настаивать на
buf_size
, Как [MSDN]:ReadEventLog
Состояния функций: при получении событий можно использовать буфер до 512 КБ. Теперь (начиная с pywin32 версии 220) его можно передать как аргумент (последний)win32evtlog.ReadEventLog
, Я был тем, кто представил его в pywin32 ( http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/rev/955b7db466fe). По умолчанию было ограничение, так что размер буфера был жестко закодирован до 1K. Так как каждыйReadEventLog
обращался к диску, с новым размером буфера я получил 10-кратное улучшение скорости (для событий ~180 КБ)
- Возвращает список
- Поскольку я храню все данные в двух списках (вместо обработки данных на месте), я выбираю скорость, а не потребление памяти. Для событий ~20К 2 списка занимают ~30 МБ ОЗУ (что в настоящее время, я думаю, достаточно прилично)
@ EDIT0: я не мог найти способ получить всю необходимую информацию с Evt*
Семейство функций, так что я получаю его из обоих источников (я улучшил сценарий, который я ранее опубликовал):
@ EDIT1: в соответствии с [MSDN]: функция OpenEventLog:
Если вы укажете собственный журнал, и он не найден, служба регистрации событий откроет журнал приложений; однако не будет никакого связанного сообщения или файла строки категории.
[MSDN]: в ключе журнала событий перечислены 3 стандартных. Вот почему он открывает журнал приложений. Я сделал несколько небольших изменений в сценарии, чтобы проверить источники. Я не знаю, откуда MMC получает события установки.