English | 中文
api-log-viewer is a Svelte 5 SPA trace viewer for LLM gateway logs. It pairs with api-log to render recorded OpenAI Chat, Anthropic Messages, Responses, and Gemini traffic. Designed as a frontend rather than THE frontend — operators can point it at any compatible JSONL or SQLite-backed trace store. Useful for stacks running sub2api, CLIProxyAPI (CPA), or new-api to inspect requests, responses, SSE chunks, tool calls, reasoning content, sessions, and replay data.
api-log-viewer 是一个面向 LLM 网关日志的 Svelte 5 SPA trace 查看器。它跟 api-log 配套,渲染录制下来的 OpenAI Chat、Anthropic Messages、Responses、Gemini 流量。设计上是一个前端而不是 THE 前端 —— 后续也可以对接到兼容的 JSONL / SQLite trace 存储。跑 sub2api、CLIProxyAPI(CPA)、new-api 的运维可以用它查看请求、响应、SSE chunk、tool call、reasoning 内容、session 和 replay 数据。
Backend recorder: api-log — Go HTTP recording proxy that captures OpenAI-compatible, Anthropic Messages, Responses, and Gemini gateway traffic into JSONL + SQLite.
Home — aggregate dashboard: capacity, capability badges, active clients (resolved from client headers), token-usage sums, traffic volume.
Traces — list view with filter sidebar (status, path with
* prefix, model, key prefix, session, since, limit) + a
keyboard-friendly row table. Selecting a row opens the detail panel.
Trace detail (Overview tab) — request / response identity, tokens in / out, duration, client + key + upstream, content shape (text / reasoning / tool-call counts). Conversation + Raw tabs sit alongside.
Plugins — operator-opt-in mutate / intercept hooks. Per-instance enable toggle, hot-reload via the backend PUT / PATCH / DELETE API (no restart). YAML defaults + runtime override layer, source pill shows which side currently owns each row.
This viewer is a thin reader on top of the api-log HTTP read API. It does
not capture, route, store, or rewrite traffic. All recording is done by
the backend; see ARCHITECTURE.md § 6 in the api-log repo for the read
API contract.
- This release tracks
api-log >= 0.1.0. The wire contract (read API + trace JSON shape) is stable across patch versions of that line. - Minor backend bumps may add fields; the viewer ignores unknown fields rather than failing closed.
The viewer talks to a single backend origin. In development it proxies
/api/* and /healthz to $APILOG_BACKEND (default
http://127.0.0.1:7862). In production the typical layout is to serve
the static bundle and reverse-proxy /api/* + /healthz from the same
hostname so there is no CORS surface.
Routing is hash-based (#/landing, #/traces, #/traces/<id>). The
bundle is a single SPA — any static file server that returns index.html
for / is sufficient; no rewrite rules are required.
pnpm install
pnpm devRuns at http://localhost:5180. Vite proxies /api/* and /healthz
to the backend at $APILOG_BACKEND (default http://127.0.0.1:7862).
Start the api-log backend first (see its Quick start), then point the viewer at it:
APILOG_BACKEND=http://127.0.0.1:7862 pnpm devOn first load the viewer asks for the admin bearer token. The backend
writes that token to ./data/admin_token on its first run; paste it
into the auth modal. It is stored in localStorage (Safari private mode
and sandboxed iframes are tolerated — module load no longer crashes if
storage is unavailable).
Five top-level pages, addressable by hash route:
| Route | Page | What it shows |
|---|---|---|
#/landing |
Home | Backend health, recent trace summary, jump-off links |
#/traces |
Traces | List + filters + detail panel for recorded traces |
#/plugins |
Plugins | CRUD UI for text-replace, text-append, path-filter plugins |
#/export |
Export | Bundle a filter set into a downloadable zip for offline analysis |
#/settings |
Settings | Theme, language (en / zh), bearer token, about |
#/traces renders the result of GET /api/traces with a sidebar of
filter inputs: status code, path (exact or prefix/*), model,
key_hash, session_root_id, since, limit. Datalist suggestions
populate from observed values in the loaded page.
Selecting a row opens the detail panel with three tabs:
| Tab | Contents |
|---|---|
| Overview | Identity, status, latency, model, tool-call summary, reasoning summary, project context extracted from AGENTS.md / CLAUDE.md if recorded |
| Conversation | Rendered messages — system / user / assistant / tool blocks, SSE replay, tool-call args + results |
| Raw | Request headers + body and response headers + body co-located, single tab so operators stop ping-ponging |
Headers and Body were folded into the Raw tab to keep diff-style
reading in one place. SSE replay is rendered inside Conversation rather
than as a separate tab.
The Plugins page lists configured plugins by category (text-replace /
text-append / path-filter), exposes per-plugin enable / edit / delete,
and hits PUT /api/config/plugins/:id, PATCH …/enabled, and
DELETE … on the backend. Backend hot-reload picks the change up
without a restart.
The Export page lets the operator apply a trace filter and download a zip bundle (traces + matching JSONL + any project-context files), so a downstream agent can ingest the bundle offline.
pnpm build
# dist/ contains the static SPA bundleThe build produces a single bundle (one HTML + hashed JS/CSS). Bundle size is auto-measured in CI at roughly 77 KB JS gzipped — small enough that aggressive CDN caching is the only required performance work.
Serve the static bundle and reverse-proxy the backend on the same origin to avoid CORS:
your.domain {
root * /opt/api-log-viewer/dist
file_server
handle /api/* { reverse_proxy 127.0.0.1:7862 }
handle /healthz { reverse_proxy 127.0.0.1:7862 }
}server {
listen 443 ssl;
server_name your.domain;
root /opt/api-log-viewer/dist;
index index.html;
location / {
try_files $uri /index.html;
}
location /api/ { proxy_pass http://127.0.0.1:7862; }
location /healthz { proxy_pass http://127.0.0.1:7862; }
}- The admin bearer token is held in
localStorage. Anyone with DOM access to the viewer origin can read it; treat the viewer origin as trusted and gate it (mTLS, VPN, basic auth at the reverse proxy) on shared deployments. - The viewer never re-issues recorded traffic to a real upstream. The Conversation tab's SSE replay is a render of recorded bytes, not a live request.
- The viewer does not perform redaction. What it shows is what the
backend recorded; see api-log's
SECURITY.mdfor the capture-side redaction posture.
| Component | Version |
|---|---|
| api-log backend | >= 0.1.0 |
| Node | >= 22 (CI matrix: 22.x) |
| pnpm | >= 9 |
| Browsers | Last two versions of Chrome, Firefox, Safari |
CI measures gzipped JS size on each build. As of 0.1.0 the bundle is
approximately 77 KB JS gzipped, 141 source files, single-bundle SPA.
pnpm install
pnpm check # svelte-check
pnpm test # tsx-driven unit tests
pnpm build # production bundle into dist/
pnpm dev # dev server on :5180CI runs check + test + build on push and pull request; release tags
on v* build artifacts. See .github/workflows/ci.yml.
Internals:
- Svelte 5 (runes) + Vite + TypeScript.
- No CSS framework, no component library, no client-side router lib.
- i18n is a dictionary in
src/lib/i18n/{en,zh}.tswith runtime switch. - All backend calls go through
src/lib/api.ts, which handles bearer injection, 401 → auth modal, and thePluginAPIError{error, detail}envelope.
- api-log — Go recording
proxy this viewer reads from. The HTTP read API contract lives in
ARCHITECTURE.md § 6of that repo. SECURITY.md— viewer-side threat model.CHANGELOG.md— release notes.
api-log— every surface in this viewer renders something the backend already recorded; the read API contract drives the shape of every page.- Svelte 5 (runes) — module-level
$statecells made the desync-prone i18n / theme / auth state surfaces tractable without a store library.
This codebase and its documentation were developed with Claude Opus 4.8 (Anthropic) as the primary pair-programmer for the Phase L UI revamp, the plugin management surface, the i18n dictionary, the localStorage-hardening pass, and these READMEs; and GPT-5.5 via Codex CLI as a research and review assistant — pre-release adversarial review, README structural analysis against reference OSS projects (CLIProxyAPI, sub2api, cc-switch), and tone audits. The cut / keep / amend judgments are the human author's; AI assistance is named here for transparency, not as authorship.
MIT — Copyright 2026 Leo Yun.



