Logs

The log stream opens application logs, access logs, audit trails and build output in the page — from a file the user drops, a URL, a stream, or lines your application appends as they happen — and makes them searchable at once. Nothing is uploaded and there is no log server: the lines stay on the machine that opened them.

It reads JSON lines, logfmt, syslog, Apache and Nginx access logs, CSV and TSV and plain text, finds the level and the time in every line, keeps each stack trace with the entry it belongs to, and draws terminal colours as colours. Search with words, "phrases", /regular expressions/ and fields — status:>=500, level:>=warn, -healthcheck — and every match is highlighted.

Masking hides email addresses, IP addresses, card numbers, tokens and keys on screen, in search and in every export, so a support engineer can read a customer's log without reading the customer. Lines are always drawn as text, never as markup: a log that carries <script> shows it and runs nothing.

An incident, two hundred thousand lines

A day of a checkout service's log, made up in this page: requests, background jobs, a database pool that runs dry just after two o'clock, and the stack traces that follow. Press a query below, or type your own. The timeline over the lines shows the errors piling up: drag across the red to show only that half hour, and Zoom out to go back. Click a level chip to hide that level. Select a line and press Enter to see its fields; press M to bookmark it. A stack trace folds away with the arrow beside it. Export saves what is on show as text, CSV or JSON.

level:>=error status:>=500 "pool exhausted" OR timeout ms:>1000 -health Clear Copy a link to this view
Keyboard: Ctrl+F goes to the search box, Enter and Shift+Enter step through the matches (F3 anywhere), the arrow keys, Page Up, Page Down, Home and End move through the lines, ← and → fold and unfold an entry, and Esc closes the details. M bookmarks a line, ] and [ step through the bookmarks. On the timeline, the arrow keys choose a bar, Shift with them several, Enter shows that time and Esc zooms out.

Fields come from the line itself. In a JSON line they are its properties; in logfmt and plain text, its key=value pairs; in an access log, the method, path, status and size; in a CSV file, its columns. status:>=500 compares numbers, path:/api/* matches a pattern, user:* finds the lines that have a user at all.

A link reopens the view. The search, the levels, the time range, the bookmarks and the chosen line are one short string: await log.getState(), and log.setState(text) to open it again. This page keeps it in its address as you work, so a copied link, or a reload, comes back to exactly what you were looking at.

Fields and columns

Six hours of an API gateway's JSON log, thirty thousand lines, laid out as columns. The fields panel on the right lists every field with its commonest values: choose ap-south-1 under region to see that one region's errors, or the minus beside a value to leave it out. The column button beside each field adds it to the table or takes it away.

Counted in the page. The values are counted over the lines on show, so they follow the search and the time range; past twenty thousand entries a sample spread through them is counted instead, and the panel says so. Values that masking would hide are never listed.

A live tail

Lines your application appends as they arrive — from a WebSocket, server-sent events, a build that is still running. With Follow on, the newest line stays in view; scroll up to read and it stops, and a button counts what has arrived since. The search and the level chips apply to new lines as they come in. This one keeps the latest twenty thousand lines and drops the oldest.

Running Burst of errors

A socket in one line. log.connect('wss://your-host/logs') follows a WebSocket, and an https: URL is read as server-sent events; the connection is opened again when it drops, after one, two, four seconds and so on, and the status bar shows where it stands. Or append lines yourself: await log.appendLog(text) takes a chunk of text or an array of lines. Listen for bmxLogFilter to count matches as they arrive — to raise your own alert on the fifth error in a minute, say.

Every format, read on sight

The same viewer, six kinds of log. Nothing tells it which is which: it reads the first lines and decides. Levels come from a JSON level, a syslog priority, an access log's status or a word in the text; times from ISO dates, epoch numbers, syslog and access-log stamps. Build output keeps its terminal colours.

JSON lines logfmt syslog Access log CSV audit trail Build output

Masking personal data

An audit trail with customers' email addresses, their IP addresses, a card number that should never have been logged, a bearer token and an API key. With masking on, none of them is shown, found by a search or written to an export; the rest of each line reads as before. Turn it off to compare, then search for @example with it on and off.

Masking on

Your own rules too. redact="email card" chooses among the built-in rules; redactRules adds your own — an account number, a patient reference — as a regular expression and the mask to put in its place. Card numbers are masked only when they pass the Luhn check, so an order number of the same length is left alone.

A million lines

Press the button to make a million lines of JSON in this page and open them. Lines are stored compactly and only the rows in view are drawn, and reading and searching run in slices between frames, so the page keeps responding throughout — type a search while it is still reading. A .gz file dropped on any of these viewers is unpacked as it is read.

Open a million lines

Press Open a million lines, or drop a log file here.