abc81e60c8
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.
58 lines
2.3 KiB
Markdown
58 lines
2.3 KiB
Markdown
# 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/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`.
|