Leño is a JSON lines log viewer with a web UI. Think of it as local Kibana/Sumo Logic for development. Works great with Pino logging library, any other app that logs into JSON, or nginx/nginx-ingress access logs.
Download the latest binary for your platform from the releases page and place it somewhere on your $PATH.
Or build from source (requires Go 1.21+ and Node.js for the Svelte build step):
git clone https://github.com/suda/leno
cd leno
npm install
make buildThis produces a single ./leno binary with the web UI embedded — no runtime dependencies.
Pipe your app's output to leno:
$ ./myapp | leno
Leno running on http://localhost:3000Now open http://localhost:3000 to see logs stream in real time.
$ LENO_PORT=8080 ./myapp | leno
Leno running on http://localhost:8080$ node server.js | lenoUse --log-format=nginx to parse nginx access logs (both the standard combined log format and the extended nginx-ingress format). Each line is converted to a JSON object before display.
# Local nginx
$ tail -f /var/log/nginx/access.log | leno --log-format=nginx
# Kubernetes nginx-ingress
$ kubectl logs -n nginx -l app.kubernetes.io/name=ingress-nginx \
--all-containers=true -f | leno --log-format=nginxThe following fields are extracted:
| Field | Type | Description |
|---|---|---|
remote_addr |
string | Client IP address |
remote_user |
string | Authenticated user (- if none) |
time |
string | Request time in RFC 3339 UTC |
method |
string | HTTP method |
path |
string | Request path |
http_version |
string | HTTP version |
status |
number | HTTP response status code |
body_bytes |
number | Bytes sent to client |
http_referer |
string | Referer header |
http_user_agent |
string | User-Agent header |
request_length |
number | (nginx-ingress) Request size in bytes |
request_time |
number | (nginx-ingress) Request processing time in seconds |
upstream_name |
string | (nginx-ingress) Upstream service name |
upstream_addr |
string | (nginx-ingress) Upstream pod address |
upstream_response_time |
string | (nginx-ingress) Upstream response time(s) |
upstream_status |
number | (nginx-ingress) Upstream response status code |
request_id |
string | (nginx-ingress) Unique request ID |
Lines that don't match either format are passed through as-is, so mixed log streams (e.g. nginx error lines alongside access lines) are handled gracefully.
$ ./scripts/generate-fake-logs.sh | lenoRequirements: Go 1.21+, Node.js (build only)
# Install JS build dependencies
npm install
# Build Svelte app and compile Go binary
make build # produces ./leno
# Development (Svelte hot-reload)
make dev # runs rollup in watch mode; run ./leno separately
# Clean build artifacts
make cleanLeño's logo is based on log by Smalllike from the Noun Project.

