Files
logviewer/README.md
T
unkin-agent abc81e60c8
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
Add logviewer: web UI for the ClickHouse log store
Single Go binary with embedded Bootstrap 3 + jQuery UI, querying logs.raw
over the ClickHouse HTTP interface as the readonly logreader user. Runs
behind oauth2-proxy; the app does no auth itself. Server-side enforced
time bounds (15m default, 72h max), parameterized queries, raw-SQL WHERE
fragment wrapped with enforced bounds and LIMIT, tail polling with a
clamped cursor, facets, healthz. Woodpecker build/test plus tag-driven
image push to artifactapi docker-internal.
2026-08-23 16:39:22 +10:00

2.3 KiB

logviewer

Web UI for the ClickHouse log store (logs.raw). Single Go binary with an embedded Bootstrap 3 + jQuery UI — no CDN assets, no build step.

logviewer does no authentication itself. It runs behind oauth2-proxy at logviewer.unkin.net; the proxy is the auth boundary. Query safety relies on the readonly ClickHouse user (logreader) plus server-side guardrails:

  • Time bounds are always enforced. Missing since/until default to a 15m window ending now; windows wider than 72h (the table TTL) are rejected. No request can scan the store unbounded.
  • Everything is parameterized (ClickHouse param_* bound parameters) except the explicit raw sql fragment, which is parenthesised and ANDed into the outer query — the enforced time bounds and LIMIT still apply, and the readonly user can't write.
  • max_execution_time=30 is set on every query.

Configuration

Env Default
CH_URL http://clickhouse-logs.logging.svc.cluster.local:8123 ClickHouse HTTP endpoint
CH_USER logreader readonly ClickHouse user
CH_PASSWORD (empty) password for CH_USER
LISTEN_ADDR :8080 HTTP listen address

API

  • GET /api/querysince/until (RFC3339, unix, or relative 15m/6h/1d), filters namespace/host/pod/container/app/severity/stream/source, q (space-separated case-insensitive substring terms, all must match), sql (raw WHERE fragment), limit (max 1000) / offset. Newest first.
  • GET /api/tail — same filters plus cursor (exclusive lower bound, unix ms). Returns ascending rows and the next cursor. Stale cursors are clamped to the last 15m.
  • GET /api/facets — top 20 namespaces / apps (labels['app']) / hosts by row count in the window.
  • GET /healthz — ClickHouse ping.

UI

Time-range picker (15m/1h/6h/1d/custom), facet dropdowns, debounced fuzzy search, toggleable raw-SQL box, paged log table with expandable rows (labels/fields), and a Tail mode (1s polling, autoscroll, pauses when you scroll up).

Development

make build   # dist/logviewer
make test    # go test -race ./...
make patch   # tag + push next vX.Y.Z → CI builds+pushes the image

Releases: pushing a v* tag builds the container and pushes it to artifactapi.k8s.syd1.au.unkin.net/docker-internal/logviewer.