Skip to content

Log Analyzer plugin

Turn the editor into a capable log workbench: parse structured log files through editable profiles, navigate by parsed fields and timestamps, merge multiple sources onto one timeline, follow a single request across sources, and analyse rates / percentiles / top-N values — all without loading the raw text into a normal editor tab.

Where to find it

The plugin adds three entries under the Log Analyzer menu:

  • Log Analyzer ▸ Open as Log… — open a single log file in a dedicated Log View window.
  • Log Analyzer ▸ Open Merged Logs… — pick two or more files, give each its own profile, and see them interleaved on one timeline.
  • Log Analyzer ▸ Open System Log… (Windows only) — query the Windows Event Log through the same view, with filters pushed down to PowerShell's Get-WinEvent.

A Preferences ▸ Plugins ▸ Log Analyzer page lets you set the default level chips, the fallback profile for files that don't match a glob, the "warn before parsing files above N MB" threshold, the default filter mode, and the follow-tail poll interval — one knob for both the file tail timer and the system-log stream (each Windows Event Log poll spawns a PowerShell process, so heavyweight sources may want it slower).

Large files parse on a background thread with a live record counter, and the expensive aggregations (message templates, anomalies) also compute in the background — the status bar shows "Analyzing statistics in the background…" and the panels fill in when ready, so the window stays responsive throughout. On very large loads the statistics aggregate over the newest 150,000 records and the timeline samples the whole span.

Single-file Log View

Opening a log shows the dedicated Log View window. It mirrors the ergonomics of the editor's own table-based windows (like the Diff Compare window) and stays out of the editor tab strip.

Layout

  • Toolbar — level chips (TRACE…FATAL), filter mode + filter line, Next / Previous Error (F8 / Shift+F8), Go to Time (Ctrl+G).
  • Timeline strip at the top — event volume bucketed over time, coloured by level. Drag a range to filter by time; click a bar to jump the table to the first record at or after that bucket.
  • Table in the middle — Time / Level / Source / Message, virtualised so it scrolls smoothly even for hundreds of thousands of records.
  • Detail pane below the table — shows the full raw multi-line entry for the selected row.
  • Stats panel docked on the right — per-level counts, event rate, percentile picker (p50/p95/p99 over any numeric field, default bytes), HTTP status-class distribution (1xx…5xx, hidden when no status field is present), and a top-N table over any field (default url, capped at 25 rows).

The record table and the detail pane use the editor font from Preferences ▸ General — the same face and size as a normal editor tab, so timestamps and levels line up column by column. Changing that setting applies to log views that are already open, no reopening needed. The same goes for the merged view, its side-by-side panes and the sequence diagram. The toolbar, filter line and statistics panel keep the interface font: they are chrome, not log content.

Filter modes

The filter line in the toolbar accepts five different expressions; pick the mode from the combo to its left:

Mode What it matches
Plain Case-insensitive substring against the raw line.
Words Whitespace-split tokens; every token must appear somewhere in the line, in any order. phpinfo GET matches a line containing both phpinfo and GET.
Wildcard * and ? glob against the raw line, case-insensitive.
Regex Plain QRegularExpression pattern, case-insensitive by default.
Query Structured grammar with field, level, and time predicates plus AND/OR/NOT and parentheses.

Examples for Query mode:

level>=WARN
status:500 AND method:POST
message~/timeout|refused/ AND @time in [09:00..09:05]
NOT (source:nginx OR source:apache)
level>=ERROR OR (status:5\d\d AND method:POST)

The filter line debounces typing by 200 ms so the table doesn't refilter on every keystroke; pressing Enter or losing focus applies immediately, and clearing the line also applies immediately. When a Wildcard / Regex / Query expression fails to compile the line turns red and the tooltip shows the diagnostic — the proxy keeps the previous valid filter on screen so the table doesn't blank out while you type.

  • F8 / Shift+F8 — next / previous error or fatal record.
  • Ctrl+G — go to a specific time (UTC, calendar popup).
  • Click a timeline bar to jump to the first record in that bucket.

These three chords are user-remappable in the Shortcut mapper (category Log Analyzer); open Log Views apply a remap immediately and the toolbar tooltips show the current chord.

Live parse + cancel

Large files start showing records within tens of milliseconds; parsing continues in the background and the table grows as records arrive. Filtering, scrolling, and F8 work while the parse is still running. Closing the window stops the worker promptly.

Merged Log View

Log Analyzer ▸ Open Merged Logs… lets you investigate a problem that spans multiple sources — a client log plus a server log, an app log plus a system journal, web access plus error, etc.

The per-file profile picker

After choosing two or more files you see a small dialog with one row per file and a profile combo per row. The combo is pre-selected by matching the file name against each profile's filename globs (case-insensitive). For a typical nginx deployment, access.log auto-picks nginx — access log (default combined) and error.log auto-picks nginx — error log. Override any row manually before clicking OK.

Layout

  • Toolbar — level chips, filter line with mode combo, trace controls (combo + Follow / Clear), Sequence diagram toggle, Side-by-side toggle.
  • Timeline strip at the top — stacked per-source histogram with one lane per source, so you can see correlated bursts visually.
  • Trace banner above the table — amber strip shown only when a trace filter is active: Following <field> = <value> — N rows across all sources.
  • Stacked main area — three pages switchable from the toolbar:
    • Table (default): one row per merged record with Time / Src / Level / Logger / Message columns; the Src column foreground is the source's lane colour so you can scan source identity at a glance.
    • Sequence diagram — see Follow trace.
    • Side-by-side panes — see Side-by-side.
  • Detail pane below the table.

Filters that compose

In the merge view the filter pipeline runs in this order, all composing on top of each other:

  1. Source visibility — a checkbox in the timeline's left margin, one per lane, next to the source's name and colour. An unchecked lane keeps its row, dimmed: it has to, since that checkbox is the only way back. Right-click the margin for Show only this source / Show all sources.
  2. Level chips — TRACE…FATAL.
  3. Follow trace — see below.
  4. Text filter — Plain / Words / Wildcard / Regex / Query (same modes and behaviour as the single-file view, applied to the merged stream).

That means you can do, for example, level>=ERROR AND status:500 across both nginx access and error logs in one expression, and follow a single client through both — the trace filter narrows the set further without breaking the level / source / text filters.

Follow trace

  1. Pick a correlation key in the toolbar's Trace: combo. It's seeded with the conventional set (trace_id / request_id / session_id / correlation_id / span_id / xRequestId) and any distinct field name actually present in the loaded records. Type a custom name if needed.
  2. Select a row in the table.
  3. Click Follow trace — or right-click the row and pick Follow trace: \<field>=\<value> for any conventional key the record carries (a one-shot path that doesn't need the combo step).

The table immediately filters to just the records sharing that key's value across all sources. Click Clear trace to remove the filter.

Sequence diagram

Once a trace filter is active and exactly 2–4 sources are visible, the toolbar's Sequence diagram toggle becomes available. It switches the main area to a fixed-lane diagram:

  • Each visible source is a vertical lane labelled at the top with a coloured swatch.
  • One row per record, time flowing top-to-bottom. The actual timestamp is on the left so you don't lose precision; rows are rank-based (fixed height) rather than time-proportional, because a real-world trace has bursts plus long pauses and a time-proportional axis would waste most of the screen on the pauses.
  • A coloured dot on the owning source's lane, plus a subtle grey arrow between consecutive records on different lanes — so you can literally see the trace bouncing between systems.
  • Level chip and message text on the right.

Click any row in the diagram to scroll the underlying table to that record. Toggle the action off to go back to the table.

For >4 sources the diagram is disabled and the table view stays.

Side-by-side panes

When exactly two sources are visible, the toolbar's Side-by-side toggle becomes available. It splits the main area into two table panes — one source each — with scroll positions linked by timestamp: scroll either pane, and the other tracks to the nearest record at or after that time. Drift between server clocks shows up naturally as the panes pulling apart.

The Sequence diagram and Side-by-side toggles are mutually exclusive (turning one on snaps the other off); both reset to the table view when the active source count moves outside their supported range.

Rotation sets are one lane

Picking three files out of one rotated set is picking one log, so they load as one lane. The picker shows a row per lane with its segments listed underneath, oldest first, and the lane is labelled after its newest file with a count of the older ones — MxAPILog.txt (+2).

Right-click a lane in the timeline for Show each rotated file separately and, once split, Merge rotated files into one lane. Splitting is a re-partition of what is already in memory, so nothing is read again — but it changes what a row means, so the table drops its selection.

Sources must state comparable times

A merge places records from several sources on one axis, which only means something if they can all state the same amount of date. A year-less syslog cannot be positioned against a dated log without inventing the year, and a build log with no timestamps cannot be positioned at all. Mixing them is refused up front, with a message naming which sources fall in which group. Merge sources of the same kind, or open them separately.

Restoring

A merged view comes back with the session. What is saved is the profile chosen for each lane and the files that lane resolved to; the records are re-read in the background, so the tab appears at once and fills in. Files that vanished and profiles that were deleted are skipped and noted in the log rather than turned into a question at launch.

Native system logs (Windows)

Log Analyzer ▸ Open System Log… queries the Windows Event Log without needing a file on disk. The dialog asks for a channel (Application / System / Security / a custom name), an optional minimum level, and a record cap. The plugin shells out to PowerShell's Get-WinEvent | ConvertTo-Json -Compress with the filter pushed down, then parses the NDJSON output through the bundled windows-event-log profile.

The action is only registered when the host actually has a working Windows Event Log source — it is genuinely absent on Linux and macOS rather than greyed.

macOS unified log and Linux journald sources are planned and slot in behind the same internal interface, with no UI changes when they land.

Profiles

The engine has no built-in special-cased parsers. Every supported format — Apache, nginx, syslog, JSON Lines, logfmt, CSV, Windows Event Log — is a LogProfile JSON file shipped in the application's log-profiles/ directory. User profiles live in the user config directory's log-profiles/ folder and override bundled ones by id.

Each profile describes:

  • line model — regex, json, logfmt, csv or delimited.
  • timestamp — which field, accepted strftime-style formats, source timezone, epoch units.
  • level — which field plus a mapping of raw tokens to the canonical Trace / Debug / Info / Warn / Error / Fatal enum.
  • continuation — how following lines glue to the current record (Java stack traces become one entry, not 30).
  • derived fields — extra fields computed by a sub-pattern over an existing field. The bundled apache + nginx profiles use this to expose a uniform client_ip field so the merge view's Follow trace can correlate web access and error events for the same client across both sources.
  • rotation — which files form one logical log, and in what order. See Rotation: describing a set of files.

You do not have to write these files by hand: the editor for them — with a field grabber, grok-style aliases and a live timestamp tester — is described under Editing profiles.

Times are shown to the precision the log states

Not every log states a full date. RFC 3164 syslog gives a month, a day and a time but no year; some embedded and in-house logs give only a time of day; build output gives none at all.

The Time column shows exactly what the line said — 13:36:55, Oct 10 14:32:52, or a full timestamp with UTC — and never fills a gap in with a guess. The same holds for the TSV export and for the profile editor's preview, so an invented year cannot leave the application and be mistaken downstream for a real one.

Ordering, the timeline and the statistics keep working inside such a file: the missing parts are anchored internally so records stay comparable with each other. They just never surface.

Editing profiles

Log Analyzer ▸ Edit Profile… lists every profile with a Built-in or Custom badge. Selecting one loads it.

Built-in profiles are read-only, the way built-in themes are: the form is shown but disabled, and Clone is the way to change one. A clone takes a free id, so a copy can never quietly shadow the profile it came from. New starts from scratch, Delete removes one of your own, and switching away from unsaved work asks first.

When a profile is imported whose id matches a built-in one, it is stored under a free id instead of replacing it — and the editor says which.

Rotation: describing a set of files

A rotated log is one logical stream cut into several files. A profile says which files belong together with two names — nothing else is needed, and because there is only one description there is nothing that can contradict itself.

Field Meaning
liveName The file being written to right now.
rotatedName The shape the older files take.
indexOrder Which end of the numbering is newest.
orderBy Where the order comes from. Almost always name.

Placeholders

Both names accept these. Outside them, every character is literal — . is a dot, not "any character", and * is an asterisk, not a wildcard.

Placeholder Means
%LIVE% The file the user opened. Use it when the profile cannot know the name in advance.
%N The rotation number. Read as a number, so _10 sorts after _9 rather than between _1 and _2.
%Y %y Year, four or two digits. Two-digit years use the usual pivot: 69–99 mean 19xx, 00–68 mean 20xx.
%m %b %B Month as a number, an abbreviation (Jul), or in full (July).
%d %j Day of the month, or day of the year.
%H %M %S Hour, minute, second — for schemes that rotate more than once a day.
%% A literal percent sign.

These are the same strftime fields the profile's timestamp formats use, so there is no second set to learn.

A compression extension (.gz, .zip, .bz2, .xz) is handled automatically and must not appear in either name: every scheme appends it last, after whatever the pattern describes.

The three states of liveName

Value Use when
%LIVE% A general-purpose profile that cannot know the name — the set is anchored to whatever file was opened.
A literal name, e.g. access.log The profile is written for one application whose live file always has that name.
Empty There is no unnumbered live file: every file is numbered or dated. IIS and java.util.logging work this way.

Worked examples

Scheme liveName rotatedName
logrotate %LIVE% %LIVE%.%N
logrotate with dateext %LIVE% %LIVE%-%Y%m%d
Apache / nginx access.log access.log.%N
IIS (empty) u_ex%y%m%d.log
log4j2 / logback, default rolling %LIVE% app-%Y-%m-%d-%N.log
Hourly rotation %LIVE% app-%Y-%m-%d-%H.log
java.util.logging (empty) java%N.log

How the order is decided

With orderBy: name — the default — the date in the name decides first, the number breaks ties within a date, and a file with no number is the live one and therefore newest. indexOrder says which way the numbers run: higher-is-older for logrotate and spdlog, higher-is-newer for Serilog, Boost.Log and anything counting upwards as it goes. The name alone cannot tell the two apart, which is why the profile has to.

The other orderBy values exist for sets whose numbers cannot be trusted. Cyclic rotation reuses the same handful of numbers for ever, so the number is a slot, not an age; first-record-time reads each file's first timestamp instead, and mtime falls back to the filesystem.

If name is chosen and the names turn out to carry neither a date nor a number, the order falls through to the first record, then to mtime, then to the plain name — and the status bar says which, because mtime is rewritten by copying, unpacking, restoring a backup or checking out a branch.

A date in a file name never becomes a record's timestamp

Dates read out of file names are used for one thing only: putting the segments in order. Records always take their timestamps from the line they were parsed from. A file called access.log-20260730 still holds records from whatever times its own lines state.

Patterns that are refused

A pattern with two %N, or with %N directly against a date field, is rejected when the profile loads and rotation is switched off for it, with the reason in the diagnostics. app_%N%Y%m%d.log against app_1220260730.log can be read several ways, and quietly picking one would mis-order the set with nothing on screen to explain it. Put any literal character between them — every real-world scheme already does.

The profile editor validates both names as you type, so a refused pattern shows up there rather than at open time.

Splitting by application run

A run is one start-to-stop life of the application, and it is not the same thing as a file: rotation by size cuts a run across several files, and a log that only rotates on size can hold several runs in one file.

If the log prints a line when it starts — Volluto logging system started…, Begin file log, a framework banner — put a regex matching it in the profile's runStartMarker. Right-clicking a lane in the timeline then offers Split by application run, and each run becomes its own lane. The entry only appears when the records really do contain more than one run.

Without a marker the boundaries can only be guessed from gaps in time, and that guess is off by default on purpose. A run is a concept of application logs; nginx, apache, syslog and container logs are continuous services with no runs at all, and a quiet night would otherwise be read as hundreds of restarts. Where the guess is used it adapts to the log's own rhythm and is discarded outright if it produces runs too short to be real.

Time running backwards inside a source is always treated as a boundary and always reported — it means either a restart or segments put in the wrong order, and both are worth knowing about.

Themes

Per-level colours come from the theme — the plugin registers a palette category per canonical level, the Theme Editor surfaces them, and any theme can override them. Switching themes updates the level column, the timeline strip, the stats panel and the sequence diagram in lockstep.

See also

  • Compare / diff — the Diff Compare window that the Log Analyzer's windows are modelled after.
  • Logging & diagnostics — the editor's own log file (separate from the Log Analyzer's view of arbitrary log formats).