# evtx: a safe Rust parser for Windows EVTX event logs, with XML, JSON and a browser viewer

> The evtx crate parses Windows XML Event Log files in Rust using only safe code, and ships an evtx_dump utility, an offline WEVT template cache and a WebAssembly viewer. It suits analysts and Rust developers who need reliable, cross-platform event log extraction, and it will not help if you need records in a fixed order without asking for it.

**omerbenamram/evtx** — A Fast (and safe) parser for the Windows XML Event Log (EVTX) format

- Repository: https://github.com/omerbenamram/evtx
- Stars: 975 · Forks: 86
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/omerbenamram-evtx

## Reading Windows event logs from a non-Windows machine

Windows event logs are XML inside a binary container, and the container format is called EVTX. Reading one usually means being on Windows, or writing a parser that knows the chunk structure, the CRC checks and the template substitution rules of the format. The evtx crate exists to remove that requirement, and it is written in Rust with the promise of being implemented using 100 percent safe Rust across all platforms Rust supports that have a standard library.

The audience is fairly specific. Security engineers pulling logs off a Windows host into a Linux pipeline, Rust developers who want typed access to event data rather than regex over text, and anyone who wants the same output on Windows, macOS and Linux from one code path. The library is published on crates.io as evtx, documented on docs.rs, and the repository also carries an MS-EVEN6.md file, which is the Microsoft specification the format is defined by, so the format details are traceable rather than folklore.

Recovery is part of the scope too. The feature list mentions support for some basic recovery of missing records and chunks, and the samples directory contains deliberately damaged inputs such as Application_no_crc32.evtx and sample_with_a_bad_chunk_magic.evtx, which is where that behaviour gets exercised.

## One intermediate representation behind the XML and JSON output

The design decision worth knowing about is the shared intermediate representation. The README states that XML and JSON outputs are both constructed directly from it, and that no xml2json conversion is performed. That detail is the difference between a parser that understands the format and a parser that converts one text format into another after the fact.

The consequence is that the two outputs come from the same parse of the record, so they agree with each other, and the cost of producing XML is not paid twice through a conversion step. The dependency list in Cargo.toml backs this up: serde with derive and serde_json with the preserve_order feature for JSON, and bumpalo, which is an arena allocator, for the hot rendering path.

Parsing itself is parallel by default. The optional rayon dependency provides the multithreading support and the feature named multithreading is enabled by default, which the library example confirms when it says the parallel version is enabled when compiling with that feature. Text handling is equally deliberate: encoding_rs handles character encoding, and a bundled utf16-simd crate is described in Cargo.toml as designed to be usable as a standalone crate, with the sonic-writeext feature opted into so it integrates with the sonic-rs writer in the hot path renderers. UTF-16 handling is the part of EVTX that most naive parsers get wrong, so this is where the engineering effort went.

## Installing evtx_dump and dumping a log for the first time

The crate ships a binary utility called evtx_dump for converting files, and installation is either a prebuilt executable or a build from source. The README points at the releases page for the latest executable, noting that releases are built for Windows, macOS and Linux as 64-bit executables only. Building from source is a single cargo command:

```bash
cargo install evtx
```

With the binary on your path, the first useful thing to do is dump a log. Without options it writes records as XML:

```bash
evtx_dump <evtx_file>
```

To switch to JSON, add the -o flag, and to write to a file rather than to standard output, combine it with -f:

```bash
evtx_dump -o json <evtx_file>
evtx_dump -f <output_file> -o json <input_file>
```

The utility also reads from standard input, which is the useful case when the log is still compressed or sitting in a pipe:

```bash
cat <evtx_file> | evtx_dump -o jsonl -
```

That jsonl form is the one to reach for when you plan to feed the output into something else, since it produces one JSON document per line.

## Batch extraction with fd, and the ordering caveat that comes with it

The README shows how evtx_dump is meant to be composed with other command line tools rather than run file by file. Pairing it with fd gives recursive batch handling:

```bash
fd -e evtx -x evtx_dump -o jsonl
fd -e evtx -x evtx_dump '{}' -f '{.}.xml'
```

The first scans a folder and dumps every EVTX file into a single JSON Lines file. The second writes an XML file next to each EVTX file it finds. When the source path needs to travel with the data, xargs and jq add it, and the README notes gxargs as the macOS equivalent:

```bash
fd -a -e evtx | xargs -I input sh -c "evtx_dump -o jsonl input | jq --arg path "input" '. + {path: \$path}'"
```

There is a catch worth planning around. By default evtx_dump tries to use multithreading, which means records may be returned out of order. If order matters, for instance when you are reconstructing a timeline, pass -t 1 to force single threaded execution, which also ensures ordering. The same applies inside the library: the same multithreading feature that makes parsing fast is the one that disturbs sequence.

The other point is that the tool is deliberately a converter. It reads a file and writes records. Anything that turns the extracted records into findings is work you still own.

## Offline template rendering through a .wevtcache file

An EVTX record does not carry its own XML structure. It references a template definition that lives inside a provider binary such as an EXE, DLL or SYS file, and if the parser cannot reach that template, the record cannot be rendered as text. This is the classic reason a log parser produces XML fragments with numeric placeholders instead of readable fields.

The workaround in evtx is to extract the templates once into an offline cache and use that cache at render time. The functionality requires building evtx_dump with the Cargo feature wevt_templates, and the README notes that release binaries may already include it. Building the cache is one command, producing a single portable .wevtcache file:

```bash
evtx_dump extract-wevt-templates --input <provider.dll> --output /tmp/wevt_cache.wevtcache --overwrite
```

Dumping with the cache is then a matter of pointing the tool at it:

```bash
evtx_dump --wevt-cache /tmp/wevt_cache.wevtcache <log.evtx>
```

The rule is narrow on purpose. The cache applies only when a record fails because of an explicit missing or corrupt template GUID, so it repairs specific failures rather than overriding the normal path. Two debugging helpers exist for the harder cases: one dumps a record's TemplateInstance substitution values as JSON Lines, and one renders a specific template GUID with substitutions to XML on standard output. The details are in docs/wevt_templates.md.

## EVTX Web runs the same core in a browser tab

There is a second front end, hosted on GitHub Pages, for people who would rather not install anything. The README describes it as a fully-featured EVTX explorer powered by the same Rust core compiled to WebAssembly, and states that everything happens locally so files never leave the machine. The repository layout supports that story: an evtx-wasm/ directory and a run_viewer.sh script sit at the top level.

The feature set is aimed at investigation rather than extraction. Drag and drop handles very large logs, parsing runs through WebAssembly with virtual-scroll rendering, and the filtering layer is faceted across level, provider, channel, Event ID and dynamic EventData fields, all backed by DuckDB-WASM. Full-text search, column management and on-the-fly JSON or XML export of the filtered set are included, along with light and dark themes and keyboard navigation. After the first load the viewer works offline.

The gap between the two front ends is worth stating plainly. The library and evtx_dump are the automation surface, with a CLI, a JSON Lines mode and a template cache. The web viewer is a human inspection surface. They share the core, but they answer different questions.

## Where evtx stops being the right tool

Several limits come from the format and the ecosystem rather than from the code. Template definitions live on the machine that produced the log, so parsing on a remote analyst workstation usually means you also need the provider binaries, or you accept records rendered without their structure. The wevt_templates feature and the cache file exist to move that dependency, but the extraction step still needs a provider DLL that you have to obtain.

The crate is also opinionated about what it will publish. Cargo.toml excludes .evtx and .dat files from the package, and the repository keeps a separate samples/ directory with damaged and edge-case logs such as issue_201.evtx and sample-with-irregular-bool-values.evtx, which are useful for testing but are not shipped in the crate.

Language choice is the other boundary. The same core is exposed to Python through pyevtx-rs, published on PyPI as evtx, so a Python shop does not need to write Rust. What it does need is the binding package, and the Python path carries the same ordering behaviour because it wraps the same core. On the licensing side the repository carries both LICENSE-MIT and LICENSE-APACHE, and Cargo.toml declares MIT/Apache-2.0, so you can pick either. The crate is at version 0.12.3, with releases v0.12.1 on 2026-06-11, v0.12.2 on 2026-06-13 and v0.12.3 on 2026-09-22, and the last push was on 2026-10-19.

## Conclusion

Adopt evtx when you want to read Windows event logs from a Linux or macOS pipeline, in Rust, without unsafe code and without giving up on the template definitions that make records readable. Do not adopt it if record order matters and you will not pass -t 1, since multithreading is on by default and the README states records may come back out of order. Check first that your provider templates can be extracted, because offline template rendering needs the wevt_templates Cargo feature, and note that the crate is dual licensed as MIT/Apache-2.0 with the LICENSE-MIT and LICENSE-APACHE files in the repository root.

## FAQ

### How do I install evtx and dump an EVTX file to JSON?

Build and install the binary with cargo install evtx, or download a release executable for Windows, macOS or Linux from the releases page. Then run evtx_dump -o json <evtx_file>, and add -f <output_file> to write to a file instead of standard output.

### Why are EVTX records out of order in evtx_dump output?

Because evtx_dump uses multithreading by default, and the README states that records may be returned out of order as a result. Pass -t 1 to force single threaded usage, which also guarantees the order.

### Does the evtx crate use unsafe Rust?

No. The feature list states it is implemented using 100 percent safe Rust, and the project carries a safety-dance badge for unsafe-forbidden in CI, alongside a build status badge for the test workflow.

### What are WEVT templates and when do I need the wevt_templates feature?

EVTX records reference template definitions stored in provider binaries such as EXE, DLL or SYS files. Rendering them offline requires evtx_dump built with the wevt_templates Cargo feature, then a cache built with extract-wevt-templates and passed through the --wevt-cache option.

### Can I use evtx from Python instead of Rust?

Yes, through the pyevtx-rs bindings, which wrap the same Rust core and are published on PyPI as evtx. The Python path shares the same ordering behaviour, since the multithreading logic lives in the core.

### Is there a way to open EVTX files without installing anything?

Yes, the EVTX Web viewer is served statically from GitHub Pages and runs the same Rust core compiled to WebAssembly. It parses locally in the browser, filters with faceted search backed by DuckDB-WASM, and works offline after the first load.

## Sources

- [Issues](https://github.com/omerbenamram/evtx/issues)
- [License: Apache-2.0](https://github.com/omerbenamram/evtx/blob/master/LICENSE)
- [omerbenamram/evtx on GitHub](https://github.com/omerbenamram/evtx)
- [README](https://github.com/omerbenamram/evtx/blob/master/README.md)
- [Releases](https://github.com/omerbenamram/evtx/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/omerbenamram-evtx
