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.
This commit is contained in:
@@ -1,3 +1,57 @@
|
||||
# logviewer
|
||||
|
||||
Web UI for the ClickHouse log store: fuzzy find, tail and SQL-filter logs (logviewer.unkin.net)
|
||||
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/query` — `since`/`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
|
||||
|
||||
```sh
|
||||
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`.
|
||||
|
||||
Reference in New Issue
Block a user