Skip to content

v26.9.15.1283

Pre-release
Pre-release

Choose a tag to compare

@jschick04 jschick04 released this 16 Sep 00:09
Immutable release. Only release title and notes can be modified.

All changes since the last stable release (v26.7.10.1209).

Highlights

  • Event timeline histogram — a toggleable timeline strip above the event table plots event volume over time and doubles as a navigator: WPA-style cursor-anchored zoom and pan, a Scope lens that drills the table into the visible time window (with undo), and Group by dimensions that split each bar into stacked bands — severity, source, event ID, task category, opcode, log, Logon Type, Ticket Encryption Type, process image, and an update/servicing Error Code dimension with curated HRESULT labels. In-view Find (Ctrl+F) matches show as markers along the top. It's hidden by default, update scenarios turn it on, and your choice is remembered. See Event Timeline Histogram below.
  • Channel readiness & in-app channel enablement — scenarios and the live-logs menu now show each channel's real state — present, enabled, or access-denied (detected via a native Win32 channel-access probe) — so a scenario that needs an unreadable channel is flagged instead of failing on launch. When EventLogExpert is already running elevated, a required-but-disabled channel gets an Enable channel button that turns it on in place; non-elevated users get an honest "restart as administrator" hint. See Channel Readiness below.
  • Find in the current view with Ctrl+F — an incremental, in-view Find bar searches the events you're already looking at and highlights matches as you type, with an inline match count, prev/next navigation (buttons, Enter / Shift+Enter, F3 / Shift+F3, with wrap-around), and Match case / Match whole word options. It searches the visible columns plus the always-on Description, scrolls the current match into view, and in grouped mode expands a collapsed group to reveal a hidden match (then re-collapses it once you move on). See Find in View below.
  • Details Pane reader view — the Details Pane gains a Details | XML | Correlation tab strip. The Details tab decodes an event's structured Event Data / User Data into named fields (arrays one per line, a hex preview for binary, muted placeholders for empty values), adds inline explanations for common values (a Logon Type decoder and a small curated Security-Auditing glossary), and offers per-field, per-section, and whole-event copy. The pane was re-laid-out into a metadata rail — identity, Level severity, and a correlation block — beside the message and data payload, and its header and tab bar were merged into a single row. See Details Pane below.
  • Severity shown by shape, not just color — every level now has a distinct icon shared by the event table and the Details Pane (Critical = filled octagon, Error = circle, Warning = triangle, Information = info-circle, Verbose = hollow circle), so severity stays legible for color-vision differences and in Windows high-contrast / forced-colors mode. See Details Pane below.
  • Within-log activity correlation timeline — a new Correlation tab in the event details pane lays out the selected event's activity as an operation timeline: the events that share its Activity ID (newest first), a header summarizing the event count, time span, and error/warning tallies, and parent/child activity chips linked through Related Activity ID. A Filter table to this activity action pivots the whole correlation into the table as a lens. See Activity Correlation below.
  • Filter lenses — transient, reversible narrowing — a new lens stack layers temporary narrowing on top of your filters without touching the saved filter or exports. From an event's right-click menu: Show Related by Activity ID, Show Events Sharing Related Activity ID, Show Parent Activity, and Show Events Near This Time (a centered ±30s … ±1h window). Active lenses appear as removable breadcrumb chips above the table, Esc pops the top lens, and a lens clears itself automatically when you close the log it came from. See Filter Lenses below.
  • Launch a scenario against a folder of exported logs — point a scenario at a folder of exported .evtx files and it opens the files whose channels the scenario targets, matched by channel name — so a curated triage scenario works against logs collected from another machine, not just the channels present on the local host. See Scenarios below.
  • Opcode and Related Activity ID event fields — events now expose their Opcode (resolved to its name, matching Event Viewer's precedence) and Related Activity ID. Both show in the reader view and are available as filter properties with value suggestions in the Basic filter editor. See Opcode & Related Activity ID below.
  • User column resolves to an account name — the event User column now turns a raw SID into a friendly account name, computed entirely offline (no LookupAccountSid), so it's fast and works against logs captured from another machine. The Details Pane shows both the resolved User and the raw User SID (when they differ), and a single unified User filter matches either the SID or the name. See User Identity below.
  • Multi-value filtering (any-of / none-of) — Basic filter rows gain operator-aware multi-select matching, so one row can match any of several values or exclude all of them, including on named Event Data and User Data fields via Contains-Any. Empty values are handled safely so a stray blank can't quietly turn a filter into a match-all, and several built-in scenarios were refactored onto these collapsed multi-value rows. See Multi-Value Filtering below.
  • Redesigned status bar — the status bar was rebuilt into a structured left / right layout: the active source and thousands-separated counts ("1,234 events", "200 of 1,234 shown" when narrowed, "3 selected" for a multi-select) plus a read-only Filtered indicator on the left; transient loading / failed / live-channel / resolver activity on the right. It also fixes a screen-reader defect where the entire bar re-announced on every count tick. See Status Bar below.
  • Sharper threat-hunting and update-triage scenarios — the built-in LOLBin and encoded-PowerShell scenarios were moved off brittle Description-text matching onto precise Event Data fields (NewProcessName, CommandLine), and the Windows Update / servicing triage scenarios were fixed and extended (servicing outcomes, reboot/crash correlation). See Scenarios below.
  • Smoother event table on large logs — the grid, timeline histogram, and status bar now render from a prepared presentation snapshot projected off a columnar event store and updated in place as events stream in, instead of recomputing sort, grouping, and derived state on every render. The render path was also decoupled from the app's state-management library, so scrolling, filtering, and live-tail stay responsive on big logs. See Performance & Memory below.
  • Filter at-rest File logs without reopening them — applying an XML-referencing filter to an opened .evtx File log used to force a full reload that pre-rendered XML for every row. The app now renders event XML on demand only for the candidate rows a filter could actually match, so the filter applies in place — faster, and without reopening the log. See Performance & Memory below.
  • Statistics panel — a docked, resizable Statistics drawer summarizes the current filtered event set and turns it into a triage surface: a severity breakdown bar plus top contributors by Source, Event ID, Task Category, and User, each with one-click include (+) / exclude (−) filtering and a searchable View all modal. Toggle it from the event-count chip in the status bar. See Statistics below.
  • Resolution & Coverage — a new view surfaces which loaded events have unresolved descriptions and why, with a live status-bar coverage tally, a per-provider breakdown, one-click cause filters, remediation hints, and a per-provider Event-ID sub-table split by severity. See Resolution & Coverage below.
  • More events resolve to the correct description — the resolver now synthesizes descriptions for self-describing (TraceLogging) events (such as Perflib and WER-PayloadHealth) that Windows itself often leaves blank, prefers that inline content over colliding legacy short-ID messages, and keeps exact zero-insert modern matches — raising local resolution from 92.6% to 95.2% with no meaningful load-time or memory cost. See Event Resolution below.
  • Status bar at-a-glance — the status bar gains an advisory Memory usage chip (color-coded as it climbs), a loading progress percentage on the loading chip (Loading: 45,000 (45%)), a clickable New Events indicator that loads buffered live-tail events on click, and a single consolidated multi-log loading indicator. See Status Bar below.

Features

  • An event timeline histogram above the table (View → Timeline) with cursor-anchored zoom, pan, a Scope drill-down lens, undo, and Group by dimensions — see Event Timeline Histogram below.
  • Group the timeline by severity, source, event ID, task category, opcode, log, Logon Type, Ticket Encryption Type, process image, or update/servicing Error Code (labeled with the hex HRESULT plus a curated CBS/WU symbol when known).
  • Channel readiness in the scenario detail view, the empty-state dashboard, and the live-logs menu — per-channel present / enabled / access-denied status tags, with an inline "requires elevation" hint on denied live channels — see Channel Readiness below.
  • Enable a disabled channel in place from the scenario dashboard when running elevated, with a confirmation that the change is persistent and machine-wide — see Channel Readiness below.
  • In-view Find (Ctrl+F) in the event table, with incremental highlighting, match count, wrap-around prev/next, and Match case / Match whole word toggles — see Find in View below.
  • A Details | XML | Correlation tab strip in the Details Pane; the Details tab decodes named Event Data / User Data fields, previews binary as hex, and explains common values (Logon Type, a Security-Auditing glossary) — see Details Pane below.
  • Per-field, per-section, and whole-event copy from the Details Pane reader view.
  • Distinct severity shape icons shared by the event table and the Details Pane (Critical / Error / Warning / Information / Verbose), so the two can't drift — see Details Pane below.
  • The Details Pane header and tab bar merged into one row (the reader tab is now labeled Details), reclaiming a row of vertical space — see Details Pane below.
  • A Correlation tab in the event details pane showing the selected event's within-log activity as a timeline of events sharing its Activity ID, with parent/child activity chips and a Filter table to this activity action — see Activity Correlation below.
  • A reversible filter lens stack driven from the event context menu: Show Related by Activity ID, Show Events Sharing Related Activity ID, Show Parent Activity, and Show Events Near This Time — see Filter Lenses below.
  • A lens breadcrumb above the event table with per-lens remove chips, a Clear all action, and scoped Esc-to-pop. Time-range lens chips include the date when the window spans more than one day.
  • Launch a scenario from a folder of exported .evtx files, matching each file's channel to the scenario — see Scenarios below.
  • Open Folder runs in the background and recurses subfolders by default — the start-page Folder… button and Open → Folder menu now scan on a background thread with a cancellable progress chip (no UI freeze), and include subfolders by default (with an Include subfolders checkbox and a Folder (top level only) menu item), so nested forensic captures such as KAPE/triage output load without drilling in by hand.
  • Opcode and Related Activity ID event fields, resolved and filterable, with value suggestions in the Basic filter editor — see Opcode & Related Activity ID below.
  • An opt-in Opcode column showing the decoded opcode name (for example "Info", "Start", "Stop"); hidden by default to match Event Viewer and enabled from the column picker — see Opcode & Related Activity ID below.
  • Offline User-column identity resolution — the User column shows a resolved account name (with the raw User SID alongside when it differs), and a unified User filter matches either the SID or the name — see User Identity below.
  • Operator-aware multi-select filtering (contains-any / none-of) on scalar, Event Data, and User Data fields, with a round-trip-stable Advanced-text form — see Multi-Value Filtering below.
  • A structured status bar with a left/right split, thousands-separated counts, a read-only Filtered indicator, and a fixed screen-reader announcement model — see Status Bar below.
  • Built-in threat-hunting and update-triage scenarios refined onto precise Event Data fields, plus a new proxied-execution LOLBin scenario — see Scenarios below.
  • A single log's table defaults to Date/Time order while the timeline is shown and Record ID order while it's hidden.
  • Classic positional events (whose <EventData> is unnamed, such as certain CAPI2 events) now label their fields Parameter N, matching Windows Event Viewer, instead of the raw %1…%N placeholders — see Details Pane below.
  • The event table, timeline, and status bar render from a prepared, incrementally-updated presentation snapshot for smoother scrolling and updates on large logs — see Performance & Memory below.
  • On-demand XML filtering for File logs — an XML-referencing filter now applies to an opened .evtx log by rendering XML only for candidate rows, instead of reloading the whole log with XML — see Performance & Memory below.
  • A docked Statistics panel with a severity breakdown and one-click include/exclude filtering on the top Source / Event ID / Task Category / User contributors — see Statistics below.
  • A Resolution & Coverage view (and live status-bar coverage chip) showing unresolved-description gaps by provider, with clickable cause filters and remediation hints — see Resolution & Coverage below.
  • Synthesized descriptions for self-describing (TraceLogging) events, so many events Windows leaves blank now show readable text — see Event Resolution below.
  • A Keep (pushpin) action on each filter-lens chip that promotes the transient lens into a persistent filter — see Filter Lenses below.
  • A clickable New Events status-bar indicator that loads buffered live-tail events, an advisory Memory usage chip, and a loading progress percentage — see Status Bar below.
  • Localization groundwork — the app now follows your Windows display language and text direction (left-to-right / right-to-left) with no in-app language setting; the Find bar is the first fully localized surface, with broader coverage to follow. English is unchanged today.
  • Automatic WinGet publishing — stable releases now publish to the Windows Package Manager automatically (via wingetcreate), so winget picks up new versions; the single multi-architecture .msixbundle covers both x64 and Arm64.

Event Timeline Histogram

  • A timeline histogram strip sits above the event table and plots event volume over time for the loaded view. It's hidden by default, update scenarios turn it on (and auto-select a relevant Group by dimension), and you can toggle it yourself from View → Timeline — your last choice is remembered across launches.
  • WPA-style navigation — cursor-anchored wheel zoom, shift-wheel or scrollbar pan, + / − / Fit toolbar buttons, and keyboard arrows / + / − / Home. The default view fits the whole event span; zooming narrows the window, reveals a thin scrollbar that matches the app, and pins the window, while live-tail re-anchors to the latest events.
  • Scope lens (drill-down) — the Scope button (or Enter while the timeline is focused) narrows the log table to the visible time window through the same filter-lens stack as the other lenses; double-click resets. Zoom is visual navigation only; Scope is the explicit filter action, kept visually distinct. An Undo button (and right-click) walks back through the zoom-window history.
  • Group by a dimension to split each bar into stacked bands: severity (Error / Warning / Normal), source, event ID, task category, opcode, log, Logon Type, Ticket Encryption Type, process image, or Error Code. Bands use the log-table highlight palette (softened to pastels), a forced-colors hatch fallback, and clickable legend toggles, with an overflow Other bucket for the long tail beyond the top categories.
  • Error Code groups update and servicing failures by their HRESULT — reading Microsoft-Windows-WindowsUpdateClient errorCode (including high-bit win:HexInt32 codes such as 0x800F0823 that sign-extend to a negative Int32) and Microsoft-Windows-Servicing codes from both EventData and the CbsPackageChangeState/ErrorCode / CbsUpdateChangeState/ErrorCode UserData paths. Each band is labeled with the 8-digit hex plus a curated CBS/WU symbol when known (for example 0x800F081F CBS_E_SOURCE_MISSING), and the same curated label now also appears in the Details Pane field explanation.
  • Find-hit markers — while in-view Find (Ctrl+F) is active, its matches are drawn as triangles along the top of the timeline.
  • Timeline-aware sort — with a single log open and no explicit column sort, the table follows Date/Time order while the timeline is shown (so it reads in the same direction as the time axis) and Record ID order while it's hidden. Combined views and explicit user sorts are unchanged.
  • Accessibility — the timeline is a focusable region whose label summarizes the whole loaded view (total events, time range, group breakdown); pan and zoom update a debounced live status with the current window's count, range, and breakdown, and keyboard bin-cursor navigation announces the focused bin — including a "spike" cue for a statistically anomalous bar, which preserves the meaning of the removed colored spike outline. Zoom depth is capped to the browser's layout limit and Escape is advertised.

Statistics

  • A docked Statistics panel summarizes the current filtered event set and turns it into a triage surface. Open it from the event-count chip in the status bar (the count with a caret) — the previous View-menu toggle folded into that chip — and it appears as a drag-resizable drawer above the status bar.
  • A severity breakdown bar covers Critical / Error / Warning / Information / Verbose / Unknown across the filtered set.
  • Top contributors are ranked by Source, Event ID, Task Category, and User, and each row has one-click include (+) and exclude (−) actions that push a reversible filter lens. The User include is exact — it matches the SID or the resolved name and drops no-user events — so "show only this user" really means only that user.
  • A per-dimension View all modal opens a searchable, cancellable full ranking, with an honest "top N of M" label when the list is capped.
  • The panel is responsive — four columns down to two down to header + severity as it narrows, never a scrollbar (rows fit the available height) — and it auto-collapses when the last log closes. The drag height is capped at 80% of the window and remembered.

Resolution & Coverage

  • A Resolution & Coverage view surfaces which loaded events have unresolved descriptions and why, so you can act on provider-metadata gaps directly from the log.
  • A live coverage chip in the status bar shows the resolved / unresolved tally and stays in sync as logs load; click it to open the full report.
  • The report gives a per-provider breakdown with resolution status and tallies, an expandable Event-ID sub-table per provider with a severity (level) split, and remediation hints on how to close each gap.
  • Cause filters turn a coverage cause (for example a specific unresolved provider) into an actual event filter with one click, and a Show only unresolved events toggle plus Copy table round out the footer.
  • The report scans the column store in a single pass (by Event ID and by level) without per-row allocation, and the modal has a robust lifecycle (per-expand cancellation, a stale-generation guard, and a retained snapshot) so retries and re-expands never race the copy path.

Event Resolution

  • Self-describing (TraceLogging) events now get a description. Events such as Microsoft-Windows-Perflib and Microsoft-Windows-WER-PayloadHealth carry their identity and fields inline (as named <EventData>) and define no manifest message, so Windows itself often renders them blank. The resolver now composes a description from that inline schema — the event name followed by one Field: value line per field — matching how Windows renders them, and filling in roughly 2,700 events per scan that would otherwise be empty.
  • Inline content wins over colliding legacy labels. When a modern provider's message table carries a short-ID string that collides on an event's low-16 ID (for example WER-PayloadHealth otherwise rendering as the level name "Error"), self-describing synthesis now takes priority, so the event shows its real inline content instead of the wrong label.
  • Exact zero-insert modern matches are kept. An event logged with empty EventData (for example Microsoft-Windows-AppModel-Runtime 219) is no longer rejected on template property-count and dropped to an ambiguous legacy message; an exact Id + Version + LogName match that carries a real message is kept, with absent inserts left as literal placeholders, as Windows does.
  • Together these raise local resolution coverage from 92.6% to 95.2%, measured against Windows' own rendering as ground truth. The inline schema is read with a cheap one-path values render (only genuine candidates — about 3.3% of events — render XML to read field-name labels), so the gain adds roughly 0% time and ~16 MB total allocation over ~127k events, and the offline database / exported-log resolution path is unchanged.

Find in View

  • Ctrl+F opens a floating Find bar over the current event view; Esc closes it, restores grid focus, and lands the cursor on the current match. Find is a read-only overlay — it never dispatches a filter, lens, sort, or selection change, it only reads the view to derive matches.
  • Incremental, debounced search as you type, across the enabled columns plus the always-on Description of the current view.
  • The match count shows inline as N/M, with prev / next navigation from the bar buttons, Enter / Shift+Enter, and F3 / Shift+F3 — all wrap around, with a screen-reader announcement on wrap.
  • Match case and Match whole word toggles in an options tray. Whole-word uses VS Code's word-boundary rule, so it stays correct for Event IDs, GUIDs, and hyphenated provider names, and queries with punctuation edges (.NET, -Auditing) remain findable.
  • The current match's cells get highlighter-yellow inline marks; every matching row gets a neutral inset ring (weight-encoded — 2px on the current row, 1px on the others), with a forced-colors / high-contrast outline fallback.
  • Group-aware navigation — Find expands a collapsed group to reveal a match and re-collapses the groups it opened once you navigate away, relinquishing ownership when you bulk- or context-toggle groups yourself.
  • Match marks are rendered XSS-safe (Blazor-escaped segments, never raw markup) and capped per cell against a pathological hit count. Global Ctrl+F routes through a singleton coordinator to the currently mounted pane, so it stays correct across WebView reloads.

Details Pane

  • A Details | XML | Correlation tab strip replaces the single XML view. The Details tab (formerly "Reader") decodes an event's structured Event Data and User Data into named fields, renders arrays one value per line, previews binary values as hex, and shows muted placeholders for empty or null fields.
  • Classic positional events show "Parameter N". Some providers (for example certain CAPI2 events) emit unnamed, positional <EventData> whose only field "names" are Windows placeholders (%1…%N). These now render as Parameter 1, Parameter 2, … — matching Event Viewer — in the Details tab, the filter field / value pickers, and column storage. A template that carries any real field name is left untouched.
  • Inline explanations annotate common values: a Logon Type decoder and a small, curated Security-Auditing glossary explain what a value means without leaving the pane.
  • Copy at every level — per field, per section, and the whole event.
  • The pane was redesigned into a two-region layout: a metadata rail (event identity, Level severity, a correlation block, and a collapsible System section) beside a payload region (the provider Message, a multi-column Event Data grid, and User Data). It collapses to a single column when the pane is narrow, uses uniform two-size typography, stacks field rows so long field names never truncate, and packs Event Data into at most three columns.
  • The header bar and the Details / XML tab bar were merged into one row, reclaiming a row of vertical space; when the pane is collapsed it shows a small "Details" label and caret so it stays identifiable.
  • User and User SID — the metadata rail shows the resolved User account name and, on a separate line when it differs, the raw User SID; whole-event copy includes both. See User Identity below.
  • Severity by shape, not just color — every level has a distinct icon shared by the event table and the Details Pane: Critical = filled octagon, Error = circle, Warning = triangle, Information = info-circle, Verbose = hollow circle (Critical and Verbose previously had no icon at all). A single shared SeverityIcon map drives both surfaces, so the table and pane can't drift, and the icons stay legible on selected / highlighted rows and in forced-colors mode.
  • Correlation buttons — Show related events and Show parent activity — reuse the same filter-lens commands the row context menu drives (see Filter Lenses).
  • The pane stays closed until you click an event, restoring the earlier committed behaviour.

Activity Correlation

  • A Correlation tab in the event details pane presents the selected event's within-log activity as an operation timeline: the events that share its Activity ID, newest first, so one operation's steps read as a single sequence.
  • A header summarizes the correlated activity — the event count, the time span it covers, and the Critical / Error / Warning tallies across the full membership — so an operation's health is legible at a glance.
  • Parent and child activity chips, linked through Related Activity ID, step you out to the parent operation or into a child, with a guard against false fusion when an Activity ID is shared or unusually large.
  • Filter table to this activity drops the correlated activity into the event table as a filter lens, so you can pivot from the timeline to the full table.
  • The timeline is built lazily — only while the Correlation tab is active — resolves each row leanly with a bounded message snippet (full descriptions never enter the page), and is stale-aware with a Refresh action; the selected event is always kept in view even when the display is capped.

Filter Lenses

  • A filter lens is a transient, reversible narrowing layer applied on top of your persistent filter. Lenses live only in derived state — they never mutate your saved filters, the filter pane, or exports — so you can zoom in, look around, and pop back out cleanly.
  • Show Related by Activity ID narrows the view to events that share the clicked event's Activity ID. Events with no Activity ID are correctly hidden rather than leaked.
  • Show Events Sharing Related Activity ID narrows to events that share this event's Related Activity ID (siblings of the same parent/correlation activity).
  • Show Parent Activity jumps to the parent activity's events — those whose Activity ID equals this event's Related Activity ID — with a distinct "Parent Activity = {guid}" chip.
  • Show Events Near This Time is a context-menu submenu offering five centered windows — ±30s, ±1m, ±5m, ±15m, ±1h — that narrow the table to [TimeCreated − d, TimeCreated + d] with inclusive bounds, so the clicked event always stays in view. The chip reads, for example, Near 2:03:22 PM ±5m; window math is done in UTC while the chip anchor renders in the grid's display timezone. (Day-scale browsing remains the persistent date range's job.)
  • Active lenses show as breadcrumb chips above the event table, each with its own remove control, plus a Clear all action and scoped Esc-to-pop. A time-range chip includes the date when its window spans more than one day, so a multi-day scope reads unambiguously.
  • Keep — each lens chip has a Keep (pushpin) button that promotes the transient lens into a persistent filter and removes the lens. The event view is preserved, the filter pane expands to reveal the kept filter, and the promotion is announced for screen readers. A "keep" turns the lens's internal exclude-of-complement form into the same positive include you'd get from Add Filter (for example Activity ID == X), and a time-window lens promotes into the pane's date range instead.
  • Lifecycle — a lens auto-clears when you close the log it originated from (tab close or close-all); ordinary filter-driven reloads never clear it. The "Show..." (transient lens) verb is kept deliberately distinct from the persistent "Exclude Events Before/After" items so the two don't get confused.

Opcode & Related Activity ID

  • Two Windows event fields now flow all the way through the reader, resolver, and columnar store to the resolved event: Opcode (its name) and Related Activity ID (a GUID), each preserving the null-vs-present distinction through columnar rehydration.
  • Opcode is resolved to its display name using the provider's opcode table first, then the standard WinMeta opcodes, then a numeric placeholder — matching Event Viewer's precedence.
  • Both are filterable (as the Opcode and Related Activity ID properties) with value suggestions in the Basic filter editor, and both appear in the Details Pane reader view.
  • The decoded Opcode is also available as an opt-in table column (for example "Info", "Start", "Stop"), mirroring the Task Category column. It's hidden by default — matching Event Viewer and leaving the default column layout unchanged — and you turn it on from the column picker; sort, group-by, copy, export, and Find pick it up automatically.

User Identity

  • The event User column now resolves a raw SID to a friendly account name, computed entirely offline — there's no LookupAccountSid call, so it stays fast and works against logs captured from another machine. When no name can be derived it falls back to the raw SID, so nothing regresses.
  • Resolution maps the well-known SIDs (SYSTEM, LOCAL SERVICE, NETWORK SERVICE, Everyone, the BUILTIN\* accounts, and more) and, for Security-audit events whose <Security UserID> is empty, derives the best-available identity from the account names the event already carries in its Event Data — preferring the Subject when it names a real principal, otherwise the Target. The name is computed once at resolve time and stored on the event.
  • The Details Pane shows both User (the resolved name) and User SID (the raw SID, only when it differs), and the whole-event copy mirrors the same name + SID pair.
  • A single, unified User filter matches either the SID or the resolved name in one predicate, gated to events that carry a user and mirrored across both filter backends. Column sort / group, the right-click Include / Exclude cell filter, and the filter value picker all understand the resolved identity, and Advanced User == null / User != null test whether an event carries a user at all.
  • Unresolved rows reuse the existing SID storage slot, so the friendlier display costs no extra memory.

Multi-Value Filtering

  • Basic filter rows gain operator-aware multi-select matching: one row can match any of a set of values (contains-any) or exclude all of them (none-of). Support is gated per field, so a property only offers the operators it can meaningfully use.
  • Multi-select works on scalar fields and on named Event Data / User Data fields via Contains-Any, and the built-in scenario catalog was refactored to use these collapsed Contains-Any rows instead of many single-value rows.
  • The filters round-trip cleanly through the Advanced-text form, so a multi-value row survives edit, save, and re-open unchanged.
  • Empty values are handled safely. Null or empty needles are stripped from Contains / NotContains comparisons (an empty needle would otherwise match every event), a filter whose values are all empty can't be saved, and the empty value is excluded from GUID-valued multi-select filters.
  • Importing a filter that contains empty-value multi-Contains comparisons now prompts Normalize / Import as-is / Cancel instead of silently importing a match-all filter, with clearer warning and removal-message wording and a de-duplicated filter-set name in the removal report.

Status Bar

  • The status bar was rebuilt into a structured left / right layout:
    • Left (persistent, silent): the active source (channel name, file base name, "All logs (N)", or group name), the counts ("1,234 events", or "200 of 1,234 shown" when narrowed, plus "3 selected" for a multi-select), and a read-only Filtered indicator whose tooltip names the mechanism ("Filter active" / "N lenses" / "Filter + N lenses").
    • Right (transient): loading, failed, live-channel, and resolver activity.
  • Lens-aware counts — the "shown" gate reads the composed applied filter (base intersected with lenses), so a lens-only narrowing correctly reports "M of N shown", while the tooltip still distinguishes the persistent filter.
  • Accessibility fix — the old bar was a single live region that re-announced its entire contents on every count tick. Now the root region is silent (counts and source update without announcing), a single visually-hidden role="status" region carries only a coarse, transition-only label (loading / load error / buffer full / continuously updating), the unthrottled "New Events: N" counter is aria-live="off", and a load error takes priority over "Loading" so a failure is announced even while another log is still loading.
  • Loading progress percentage — while a log loads, the loading chip shows real progress (Loading: 45,000 (45%)) using the log's record count as the denominator and processed (resolved + failed) records as the numerator, so long loads of large logs converge instead of showing only a running count. The percentage is clamped to 0–99 and falls back to count-only when no total is known. (The Failed badge now also counts events that throw during resolution — previously those vanished silently.)
  • One consolidated multi-log loading indicator — opening a folder or several files at once no longer stacks one Loading: <events> span per log. It collapses to a single Loading: N (one log), Loading N logs… (several), or Loading… before the first count, with a single Failed: N appended only when there are unreadable records. Setting a resolver status no longer wipes the other logs' in-flight loading state, so the indicator stops blinking as sibling logs finish.
  • Clickable New Events — the New Events: N indicator (shown for a live channel when Continuous Update is off) is now a button: click it to load the buffered live-tail events, the same as View → Load New Events. It stays enabled at zero (a no-op) so keyboard focus is never dropped, and it coexists with the Buffer full chip — handy since live watchers pause until the buffer drains.
  • Advisory Memory chip — a Memory: {managed} chip on the right shows the app's managed-heap usage, rendering plainly at Normal and as an AA-contrast colored fill with the level word (Elevated / High, plus a forced-colors fallback) as usage climbs, so heavy multi-log sessions are visible at a glance. Bands derive from the RAM that was actually available to the app, a 1 Hz sampler reads the heap with no forced GC, and closing a log schedules one non-blocking background collection so the reclaim becomes visible. The chip is advisory only — there's no automatic pause or partial-load.
  • Event-count chip toggles Statistics — the left-side event-count now carries a caret and opens the Statistics drawer (see Statistics above).

Scenarios

  • Launch from a folder of exported logs — a scenario can now open a folder of exported .evtx files, matching each file's channel to the scenario and opening the matches in a fresh filtered view. This works for channels that aren't present on the local host (for example, logs collected from another machine). The folder scan runs off the UI thread, and a status chip in the dashboard masthead reports its progress (Scanning… → Cancelling… → Opening logs…) with a Cancel control, so a slow scan over a large or network folder can be stopped without leaving the app.
  • The built-in LOLBin and encoded-PowerShell threat scenarios were retired from brittle Description-text matching in favor of precise Event Data-field scenarios that match on NewProcessName and CommandLine.
  • A new proxied-execution LOLBin scenario matches on CommandLine, and encoded-PowerShell coverage now includes the -enc abbreviation.
  • Update-triage scenarios fixed and extended — the servicing-outcomes view now keys on the real Servicing events (which log at Information, so the prior Level=Error filter matched nothing), and the reboot/crash correlation view was corrected and extended. Update scenarios also turn the timeline on and pre-select a relevant Group by dimension.
  • Softer highlight colors — scenario row-highlight colors were retuned to a pastel palette (also used by the timeline's grouped bands) so multi-filter color-coding is easier on the eyes.
  • Scenario comparisons that would resolve to no values are now rejected rather than applied as an empty (match-all) comparison.

Channel Readiness

  • For every channel a scenario references, the app now determines presence, enablement, and accessibility — including access-denied detection via a native Win32 channel-access probe — and surfaces that state wherever you launch logs.
  • Status tags appear in the scenario detail view, the empty-state dashboard, and the live-logs menu. A scenario with any required channel that's degraded (access-denied, not present, or failed) is treated as non-launchable, the same as a missing log.
  • Access-denied live channels stay clickable in the menu and, when you pick one, surface an inline "Log requires elevation" alert that points you to relaunch as administrator (there is no in-app elevation or relaunch-as-admin path).
  • Enable a disabled channel in place — when EventLogExpert is already running elevated, a required-but-disabled channel gets an Enable channel button that turns it on through the native channel-config write path (EvtOpenChannelConfig → set Enabled → EvtSaveChannelConfig), without an elevated helper, IPC, or relaunch. Non-elevated users instead get an honest hint to restart as administrator.
  • Safety — the enable action is fail-closed (a failed or non-Boolean read is never treated as "disabled"), the Application / System / Security channels are excluded (Windows forbids toggling their Enabled), and a confirmation names the channel and warns that the change is persistent and machine-wide (and, for analytic/debug channels, that records may be cleared). After a successful enable, the full channel-readiness set is re-fetched rather than a single-channel reprobe.

Performance & Memory

  • Ordered-view incremental render engine — the event table, timeline histogram, and status bar now render from an immutable presentation snapshot projected off a chunked columnar event store and an incrementally-ordered view, instead of recomputing sort / group / derived state on every render. The snapshot is updated in place as events stream in, so large logs scroll and live-tail with less work per frame.
  • Render path decoupled from the state library — the presentation projection is exposed through a front-end-agnostic source (OrderedViewPresentation via IOrderedViewSource), and the UI reads runtime state through agnostic read-sources and change-notifiers, so Fluxor was dropped from the UI layer (it remains the underlying state-management substrate). This removes per-render selector overhead and keeps the render path independent of the UI framework.
  • Large-log index builds are bounded and cancellable — the ordered-view bulk index build now estimates its peak transient memory and falls back to a bounded-memory insert path when a rebuild would exceed available headroom (and always for unbounded Keyword joins), so a very large re-sort can't run the process out of memory. The sort itself uses a cancellation-checking introsort, so a superseded build stops promptly instead of finishing a multi-second sort, and the ordered-view tail replay is bounded with a forward-progress fallback that prevents both owner-thread stalls and a sustained-append livelock.
  • On-demand XML filtering skips the reload — applying an XML-referencing filter to an at-rest File log previously forced a full reload that pre-rendered XML for every row. The filter's cheap (non-XML) conditions now narrow the candidate rows first, and event XML is rendered on demand only for those candidates, so the filter applies without reopening the log. A pure-XML filter (or XML under OR / NOT) conservatively treats every row as a candidate so no match is missed, and a newer filter cancels an in-flight scan mid-render.
  • Interactive work stays responsive under heavy analytics — a shared, priority-aware CPU-work scheduler now admits background analytics (Statistics, histogram, on-demand XML filter-match) behind latency-critical interactive work (in-view Find) rather than letting them compete on equal footing or oversubscribe the cores. At most one item per logical processor runs at once, a small interactive reserve engages only while interactive work is present (and collapses otherwise, so a single un-interacted log still uses every core), and running work is never suspended. In benchmarks this holds Find's p95 latency flat under multi-log load — where it previously degraded 2–4× — at ≤4% throughput cost, and is a no-op for a single log.
  • Large logs no longer freeze the UI while finalizing — the final load step (building the columnar store and updating counts) now runs off the UI thread, so opening a very large log paints instead of hanging, and the redundant per-partial reducers were removed (the final step already recomputes the same end state) to cut load-time work.

Reliability

  • Fixed an unsafe-publication data race in the columnar event store's EventColumnView key index. The lazily-built lookup is now published with a Volatile.Read + Interlocked.CompareExchange (matching the store's other caches) instead of a bare ??=, so a concurrent reader on a weak-memory CPU (Arm64) can't observe the reference before the dictionary's writes are visible. The build is idempotent, so a benign build-race is harmless. Added direct sort-kernel tie-break regression tests alongside the fix.
  • Dependency and security updates — resolved CVE-2026-54570 (AngleSharp mutation-XSS, pulled in transitively through the bUnit test dependency) by upgrading bUnit to 2.9.0, which brings a fixed AngleSharp, so the temporary version override was removed; refreshed the existing CVE-2025-6965 SQLite override, and applied the NSubstitute 6.0 test-framework upgrade and routine framework/package bumps. The solution reports no vulnerable packages.
  • Saves are written atomically — the file-save path now writes to a temporary file in the destination folder and commits with an atomic move, rather than an in-place replace with documented partial-failure states, so an interrupted save can't leave a truncated file or delete the original.
  • Background I/O across the app now shares a single parallelism limit, and import classification runs in a single pass over the entries.
  • Live-tail shows its first event immediately — the live-tail coordinator now flushes its first sparse event right away regardless of how long the process has been running, so a freshly opened quiet channel no longer appears to lag before its first new event appears.
  • Modal close requests no longer coalesce across modals — a force-closed modal's in-flight close can no longer swallow a newer modal's close request, so dialogs open and close predictably in rapid succession.
  • Fixed stale ordering after a log is re-resolved in place — when a log was rebuilt in place at a higher content version (a re-resolution rather than an append), the published event order and filter could stay stale, and a re-resolution that removed rows could momentarily reference out-of-range rows. The ordered view now rebuilds on every in-place replace — same-count, grow, shrink (down to empty), a log entering the current scope, and the coalesced seed path — and quarantines publishing until the rebuilt index is adopted, so ordering stays correct and row-removing re-resolutions are crash-safe.
  • Hardened the live-tail watcher against close/reopen races — watched logs are now tracked by a stable log id rather than by name, so a stale close can't drop a same-name log that was just reopened under a new id, a same-id add is a no-op, and a different-id add cleanly replaces the previous watcher. Watcher disposal always runs off-lock, and a displaced watcher's teardown is awaited so a superseded close still completes. A follow-up also fixed the restart gate so replacing the sole watched log while the live buffer is full stays paused (respecting the cap) yet still restarts when the replaced log was actively being watched.

Bug Fixes

  • File and folder pickers work when running elevated — File → Open, File → Open Folder, and every Save previously failed with a COMException (0x80004005) when EventLogExpert ran elevated, because the WinRT picker's broker path is unavailable to elevated callers. Every picker now routes through the procedural Win32 dialogs, so open, open-folder, and save work for standard-user, unpackaged-elevated, and MSIX-packaged-elevated processes alike.
  • Live-tail "Load New Events" now renders the new row immediately — clicking View → Load New Events (with continuous updates off) cleared the "New Events" count but didn't show the new row until a full reload. The display view is now rebuilt after the new events are ingested, so the row appears at once; this also removes a one-event lag on the continuous-update live-tail path.
  • A completed export is no longer discarded by a late Cancel — clicking Cancel in the tiny window after an export finished writing but before it was committed used to throw away the finished file and report it "canceled". Cancellation is now scoped to the write phase: a Cancel during the write still discards the partial file, but a Cancel after the write completes is a no-op and the finished export is saved (matching Event Viewer).
  • Fixed a brief flash of stale highlight styling in the scenario histogram when the last highlight color is cleared (or the eligible-filter count crosses its limit) — the group-highlight masks and the matching screen-reader announcement are now invalidated synchronously, so a re-render in that gap no longer maps the old masks through the new filter list.
  • Classic events from driver .sys message files now resolve to the right message — a legacy event from a provider whose registry EventMessageFile points at a driver's own .sys module (for example some Wi-Fi and other hardware drivers) previously showed "No matching message found" or borrowed an unrelated template's text, because the app only read .dll / .exe modules and matched legacy messages by their 16-bit short ID. It now reads .sys message tables (mapped read-only, never executed) and matches by the full 32-bit ID with its qualifier, rejecting a message whose qualifier proves it isn't this event's — so these events read the same as Windows Event Viewer. If you rely on a pre-built provider database, regenerate it to pick up the driver .sys messages.
  • Active filter-lens chips are styled again — the lens breadcrumb chips (and their remove "×" button) referenced CSS class names that no longer matched the scoped stylesheet, leaving them unstyled; the markup was realigned to the stylesheet.
  • The modal prompt now preserves the value you typed when you take the dialog's secondary action, instead of discarding it.
  • An empty value in a multi-select filter can no longer turn the filter into an unintended match-all — empty needles are normalized out, all-empty filters can't be saved, and empty-value imports prompt before applying.
  • The empty-value import removal report no longer lists a filter-set name more than once.