- ds
- Operations
- Log viewer
Log viewer
Levels, timestamps, filters and a live tail.
- INFOcheckout-apiPOST /v1/orders 201 142ms order_id=ord_9f2k4x
- DEBUGevent-relaypicked 50 events from queue webhooks.outbound lag=1.2s
- INFOevent-relaydelivered evt_71ab to https://hooks.acme.in/orders 200 88ms
- WARNevent-relaydelivery to https://erp.example.net/hook timed out after 10s, retry 3 of 8 in 4.1s
-
ERRORevent-relaygiving up on evt_70f2 after 8 attempts: connection refused
- endpoint
- https://erp.example.net/hook
- attempts
- 8
- first_attempt
- 2026-10-11T08:46:12Z
- trace_id
- 4bf92f3577b34da6a3ce929d0e0e4736
- INFOcheckout-apiGET /v1/orders/ord_9f2k4x 200 31ms
- DEBUGledger-writerbatch committed rows=212 duration=18ms
- 1,204 lines hidden by the filter
Tailingap-south-1 · last 15 minutes2,881 lines
Show code
<div class="log">
<div class="log-bar">
<div class="seg sm"><button aria-pressed="true">All</button><button>Warn</button><button>Error</button></div>
<input class="inp sm mono" type="search" value="retry" aria-label="Filter lines" />
<button class="btn xs on" aria-pressed="true"><span class="status-dot live"></span>Live</button>
</div>
<ol class="log-rows" role="log" aria-label="Logs, newest at the bottom">
<li class="log-row" data-lvl="info"><time>14:32:06.981</time><span class="lvl">INFO</span><span class="src">checkout-api</span><span class="msg">POST /v1/orders 201 142ms order_id=ord_9f2k4x</span></li>
<li class="log-row" data-lvl="debug"><time>14:32:07.004</time><span class="lvl">DEBUG</span><span class="src">event-relay</span><span class="msg">picked 50 events from queue webhooks.outbound lag=1.2s</span></li>
<li class="log-row" data-lvl="info"><time>14:32:07.118</time><span class="lvl">INFO</span><span class="src">event-relay</span><span class="msg">delivered evt_71ab to https://hooks.acme.in/orders 200 88ms</span></li>
<li class="log-row" data-lvl="warn"><time>14:32:07.412</time><span class="lvl">WARN</span><span class="src">event-relay</span><span class="msg">delivery to https://erp.example.net/hook timed out after 10s, <mark>retry</mark> 3 of 8 in 4.1s</span></li>
<li>
<details class="log-x" open>
<summary class="log-row" data-lvl="error"><time>14:32:07.498</time><span class="lvl">ERROR</span><span class="src">event-relay</span><span class="msg">giving up on evt_70f2 after 8 attempts: connection refused</span></summary>
<dl class="log-fields">
<dt>endpoint</dt><dd>https://erp.example.net/hook</dd>
<dt>attempts</dt><dd>8</dd>
<dt>first_attempt</dt><dd>2026-10-11T08:46:12Z</dd>
<dt>trace_id</dt><dd>4bf92f3577b34da6a3ce929d0e0e4736</dd>
</dl>
</details>
</li>
<li class="log-row" data-lvl="info"><time>14:32:07.530</time><span class="lvl">INFO</span><span class="src">checkout-api</span><span class="msg">GET /v1/orders/ord_9f2k4x 200 31ms</span></li>
<li class="log-row" data-lvl="debug"><time>14:32:07.611</time><span class="lvl">DEBUG</span><span class="src">ledger-writer</span><span class="msg">batch committed rows=212 duration=18ms</span></li>
<li class="log-gap">1,204 lines hidden by the filter</li>
</ol>
<div class="log-foot"><span class="status"><span class="status-dot live"></span>Tailing</span><span>ap-south-1 · last 15 minutes</span><span>2,881 lines</span></div>
</div>A log stream with the newest line at the bottom, the way a terminal does it. Columns are fixed, so timestamps and levels line up and the eye can run down them. Errors get a red wash across the whole row, warnings only colour their level; debug lines drop back to muted. Search matches are marked in the amber wash. A row with structured fields opens in place to show them.
Wrapping
- WARNevent-relaydelivery to https://erp.example.net/hook timed out after 10s, retry 3 of 8 in 4.1s; the endpoint has failed 41% of deliveries in the last hour
Code
<div class="log wrap" style="--log-h:8rem">
<ol class="log-rows">
<li class="log-row" data-lvl="warn"><time>14:32:07.412</time><span class="lvl">WARN</span><span class="src">event-relay</span><span class="msg">delivery to https://erp.example.net/hook timed out after 10s, retry 3 of 8 in 4.1s; the endpoint has failed 41% of deliveries in the last hour</span></li>
</ol>
</div>By default a long message truncates so every line stays one line. .wrap on the viewer wraps messages instead, keeping the columns.
| Class | On | What it does |
|---|---|---|
.log | div | The viewer: bar, rows, foot. .wrap wraps long messages. --cols sets the column widths. |
.log-bar | div | Toolbar: a level .seg, a filter .inp, and actions pushed right. |
.log-rows | ol | The scrolling lines. --log-h sets the height (22rem). Use role="log". |
.log-row | li, summary | <time>, .lvl, .src, .msg. data-lvl: debug, info, warn, error. |
.log-x | details | A row that opens. Its summary is a .log-row; the .log-fields list follows. |
.log-fields | dl | Structured fields as key and value, aligned under the message. |
.log-gap | li | Lines hidden or skipped, on a hatched row. |
mark | .log | A search match. |
.log-foot | div | Live state, scope and counts. |
Do
- Milliseconds in timestamps, and say the timezone once in the foot or the bar.
- Keep the scroll pinned to the bottom while tailing; stop following as soon as someone scrolls up, and say so.
- Show how many lines a filter hides.
Don't
- Colouring info lines. Most lines are info; colour is for the few that matter.
- Wrapping by default. Ragged rows are hard to scan.
- Rendering 100,000 rows. Virtualise past a few thousand.
Accessibility
role="log"makes new lines polite announcements. For a fast stream, pause announcements and offer a button to read the latest error.- The level is a word in its own column, so colour is never the only signal.