evtx
Read binary Windows event logs (.evtx, the files in C:\Windows\System32\winevt\Logs) straight from the file, with no Windows needed. Each event becomes the same kind of record the logs library makes for Windows exports: EventID, time, computer, channel, provider, the EventData fields by name (TargetUserName, IpAddress, CommandLine...), plus action/description/user/ip/success worked out the same way, so the log-triage rules and Sigma rules run unchanged. Parses the binary XML (chunks, templates, substitutions) itself. Times are read as UTC and shown in local time like logs does.
import evtx
Example
import evtx
# apps/data/sample.evtx is a small made-up Windows Security log.
let records = evtx.read("apps/data/sample.evtx")
say(f"{len(records)} events")
for r in records.slice(0, 5) {
say(f"{r.when} {r.EventID} {r.action} {r.user}")
}
say(evtx.summary(records))
Functions
read(path, options?)
returns listEvery event in an .evtx file, as a list of records.
Options
limitstop after this many events.xmltrue adds the event's XML as "xml".stricttrue fails on a broken record. Without it, a broken record is kept as a placeholder with "broken": true.recovertrue also carves old or deleted records out of the chunks' slack space. They come back with "recovered": true; Windows itself no longer shows them.
Fields of each record
- "time", "when"
- "event_id", "action", "description"
- "user", "ip", "success"
- "computer", "channel", "provider", "level_name", "record_id" (Windows' EventRecordID), "record_number" (position in the file)
"task_name", "opcode_name"when known (Security task categories, standard opcodes)."event_data"a map of the EventData fields. Every EventData field is also its own key."partial"true when parts of the event used binary XML this reader doesn't know."raw"the XML.
A .gz or one-file .zip of an .evtx file is unpacked as it is read.
Errors
- more than 500,000 events (without a smaller limit): use evtx.each().
- a .gz or .zip that unpacks to over 2 GiB, or over 100x its packed size.
Visualizehow evtx.read works
evtx.read("apps/data/sample.evtx", {"limit": 1}) gives [{"time": 1773302401, "when": "2026-03-12 08:00:01", "when_utc": "2026-03-12 08:00:01", "…evtx.read("apps/data/sample.evtx", {"limit": 3}) gives [{"time": 1773302401, "when": "2026-03-12 08:00:01", "when_utc": "2026-03-12 08:00:01", "…evtx.read("apps/data/sample.evtx", {"limit": 5}) gives [{"time": 1773302401, "when": "2026-03-12 08:00:01", "when_utc": "2026-03-12 08:00:01", "…
Drawing…
Real result: worked out by running this example with TB while the site was built.
parse(data, options?)
returns listLike evtx.read() for EVTX bytes already in memory.
Visualizehow evtx.parse works
evtx.parse(data, {"limit": 2}) gives [{"time": 1773302401, "when": "2026-03-12 08:00:01", "when_utc": "2026-03-12 08:00:01", "…
Real result: worked out by running this example with TB while the site was built.
each(path, fn, options?)
returns numberCall fn(record) for every event, one at a time, streaming the file from disk a chunk at a time, so logs of any size use little memory. Takes the same options as read(). Return false from fn to stop early. Gives back how many events fn saw.
Visualizehow evtx.each works
evtx.each("apps/data/sample.evtx", r => ids.push(r.event_id)) gives 24
Real result: worked out by running this example with TB while the site was built.
summary(path_or_records)
returns mapAn overview of an event log. Give it a path or the records from evtx.read().
Fields
- "records", "first_when", "last_when"
"event_ids"a map of id -> count.- "actions", "channels", "computers", "providers"
- "failures", "users", "ips"
- "chunks", "version", "dirty", "checksums_ok"
Visualizehow evtx.summary works
evtx.summary("apps/data/sample.evtx") gives {"records": 24, "broken": 0, "first": 1773302401, "last": 1773302542.1, "first_when": "20…
Real result: worked out by running this example with TB while the site was built.
info(path)
returns mapThe file's own bookkeeping.
Fields
- "version", "chunk_count", "next_record_id"
"dirty"true if the file wasn't closed cleanly.- "full", "checksum_ok", "records", "checksums_ok"
"chunks"a list of maps, one per chunk, with:- "index", "first_record_id", "last_record_id", "records"
- "header_checksum_ok", "records_checksum_ok"
Visualizehow evtx.info works
evtx.info("apps/data/sample.evtx") gives {"first_chunk": 0, "last_chunk": 0, "next_record_id": 25, "version": "3.1", "chunk_count"…
Real result: worked out by running this example with TB while the site was built.
is_evtx(path)
returns boolTrue if the file starts with the EVTX signature (ElfFile).
Visualizehow evtx.is_evtx works
evtx.is_evtx("apps/data/sample.evtx") gives trueevtx.is_evtx("apps/data/sample.pcap") gives false
Real result: worked out by running this example with TB while the site was built.
to_json(record)
returns textOne record as a JSON line (the same shape Get-WinEvent | ConvertTo-Json gives), handy for feeding other tools.
Visualizehow evtx.to_json works
evtx.to_json(r) gives "{\"time\": 1773302401, \"when\": \"2026-03-12 08:00:01\", \"when_utc\": \"2026-03-12 08:…
Real result: worked out by running this example with TB while the site was built.
Values
| Name | Value | What it is |
|---|---|---|
evtx.levels | {0: "LogAlways", 1: "Critical", 2: "Error", 3: "Warning", 4: "Information", 5... | Level numbers to names: 1 Critical, 2 Error, 3 Warning, 4 Information, 5 Verbose. Visualizehow evtx.levels works
No inputs: call it as it is. Real value, read from TB while the site was built. |