Lightweight, real-time log viewer for network operations teams. Stream netlog (network device syslog), srvlog (server syslog), and application logs to your browser with a single Go binary and PostgreSQL.
Live Demo — try it without installing anything.
- Real-time first — events are pushed to the browser via Server-Sent Events the moment they hit the database. Per-client server-side filtering means you only receive what you asked for.
- Lightweight stack — PostgreSQL (TimescaleDB) + a single Go binary + rsyslog. No Elasticsearch, no JVM, no agent fleet to manage.
- Three log feeds — netlog (network device syslog), srvlog (server syslog), and applog (HTTP ingest) each have independent streams, filtering, retention, and dashboards. All three are always on; optional subsystems (notifications, AI analysis, LDAP, Netbox) are the real toggles.
- Multiple clients — a browser SPA in this repo, plus a terminal UI and SSH-served TUI in the companion taillight-tui repo — all consume the same SSE streams.
- Built-in alerting — Slack, webhook, email (SMTP), and ntfy channels. First matching event fires immediately; repeats during a silence window collapse into a single digest, with per-channel rate limiting, circuit breakers, and bounded delivery retry.
- Optional AI analysis — daily log briefings generated by a local Ollama LLM, surfacing anomalies and trends without sending data off-premise.
- SSE push for netlog, srvlog, and application log events — no polling
- Filter by host, facility, severity, program, service, component, and full-text search
- Cursor-based pagination for historical queries
- Severity breakdown, top hosts, and recent high-severity events at a glance
- Per-host volume charts with selectable time ranges
- Per-device detail views for netlog, srvlog, and application logs
- Continuous aggregates for fast dashboard queries
POST /api/v1/applog/ingest— structured JSON batch endpointtaillight-shipper— standalone binary that tails log files and ships them to the ingest APIpkg/logshipper— Goslog.Handlerfor shipping your application's own logstaillight-sdk— Pythonlogging.Handlerfor shipping logs from Python apps (pip install taillight-sdk)
- A terminal UI client (
taillight-tui) and an SSH server that hosts it (taillight-wish) live in the companion repo taillight-tui — both are HTTP/SSE clients of this API
- Slack, webhook, email (SMTP), and ntfy backends
- Rule matching by hostname, programname, severity/facility/msgid (syslog), service/component/level (applog), and full-text search
- Fingerprint model: first match fires immediately, repeats within a silence window accumulate into a single end-of-window digest, silence grows linearly on sustained firing up to
silence_max - Optional
coalescebatches simultaneous first-alert bursts into one message with a real count - Per-channel token-bucket rate limit, circuit breaker, and bounded retry (
5s → 30s → 2m → 10m) - Notification log with delivery status, rendered payload, and Prometheus counters for evaluated/matched/sent/suppressed
- Scheduled daily reports via local Ollama LLM
- Anomaly detection and trend summaries
- On-demand analysis trigger via API
- Applog daily brief for service owners: ranks services by new error signatures and rate change against a 7-day baseline, scoped per service
- Postgres aggregates the logs first, so the model narrates a compact summary rather than raw lines — see
api/internal/analyzer/README.mdfor how the pipeline works
- On the netlog detail page, IP addresses, prefixes, AS numbers, interfaces, and the source device are looked up against Netbox and shown as cards alongside the event
- Lookups happen lazily after the page renders — list views and live SSE never call Netbox
- In-memory TTL cache (default 10 min, including negative results) keeps repeat detail-page visits free
- Per-entity errors surface inline; if Netbox is unreachable or disabled the panel hides itself, the detail page is unaffected
- Configurable auth scheme — legacy
Token <key>(default) or OAuth-styleBearer <key> - Disabled by default; configure under
netbox:inapi/config.ymland provide the token viaNETBOX_TOKEN
- 19 built-in color themes — Tokyo Night, Dracula, Catppuccin, Nord, Solarized, Gruvbox, SynthWave 84, and more
- 15 dark themes and 4 light themes
- One-click switching from the settings menu, persisted to localStorage
- Every theme includes matched chart colors for consistent data visualization
- TimescaleDB hypertables with compression and configurable retention policies
- Prometheus metrics (
/metricsendpoint or dedicated metrics server) - Session-based auth with API key support (read, ingest, admin scopes)
- Interactive API documentation at
/api/docs(Scalar/OpenAPI) - Docker Compose one-command deployment
- Juniper netlog reference data import (XLSX)
Netlog path (network devices):
rsyslog ──ompgsql──► netlog_events ──pg_notify──► Listener (LISTEN/NOTIFY)
(TimescaleDB) │
▼
NetlogBroker ──► SSE ──► Browser
│
▼
NotificationEngine
(Slack/Webhook/Email/ntfy)
Srvlog path (servers):
rsyslog ──ompgsql──► srvlog_events ──pg_notify──► Listener (LISTEN/NOTIFY)
(TimescaleDB) │
▼
SrvlogBroker ──► SSE ──► Browser
│
▼
NotificationEngine
(Slack/Webhook/Email/ntfy)
Application log path:
HTTP POST ──► Ingest Handler ──► applog_events ──► AppLogBroker ──► SSE ──► Browser
(API key) (TimescaleDB) │
▼
NotificationEngine
Netlog and Srvlog share the same pipeline but use separate tables, brokers, and retention policies. rsyslog writes to netlog_events or srvlog_events via ompgsql. A trigger fires pg_notify('<feed>_ingest', id). The Go backend holds a persistent LISTEN connection, fetches the full row, and the corresponding broker fans out to SSE clients with per-client filtering. Netlog events are enriched with Juniper reference data when available, and the netlog detail page additionally pulls device/IP/prefix/ASN/interface context from Netbox on demand.
Why two syslog feeds? Network devices (Juniper, Cisco, Arista) and servers (Linux, Docker, PostgreSQL) produce fundamentally different log profiles. Splitting them lets you apply independent retention policies, notification rules, and analysis. Both feeds (and applog) are always enabled; a feed you don't point logs at simply stays empty.
Application logs: apps POST JSON batches to /api/v1/applog/ingest. The handler inserts into applog_events and broadcasts directly to the AppLogBroker — no LISTEN/NOTIFY. The broker applies per-client filters and pushes matching events to SSE clients.
Network devices — especially Juniper gear — emit thousands of syslog messages per minute: chassis polls, SNMP traps, session logs, scheduler ticks, and routine daemon output. Taillight ships a modular rsyslog configuration that applies layered filters to drop the noise before it hits the netlog database.
UDP/TCP input (514, 1514)
→ parse RFC 5424 structured data
→ capture critical severity (emerg/alert/crit) before any filtering
→ filter by msgid (fastest — exact event name match)
→ route UI_COMMIT events (Oxidized, commit log)
→ filter by programname (daemon-level drops)
→ filter by facility (local7 info noise)
→ filter by severity (drop debug globally)
→ filter by hostname/IP (optional per-host rules)
→ output to PostgreSQL, LibreNMS, per-host files, remote forwarding
Filters are applied cheapest-first. Each layer has exception keywords — messages containing error, fail, critical, down, denied, or alarm pass through even if the event type is normally dropped.
| Layer | File | Method | Drops |
|---|---|---|---|
| msgid | filters/05-by-msgid.conf |
Exact $msgid match |
Chassis polls, RPD scheduler, RT_FLOW sessions, SNMP traps, LLDP neighbor-up, PFE stats, license checks |
| Commit routing | filters/06-ui-commit-trigger.conf |
$msgid match |
Routes UI_COMMIT to Oxidized and commit log |
| Program | filters/10-by-programname.conf |
$programname match |
cron, ntpd, mib2d, dcd, lacpd, cosd, alarmd, sshd, pfed |
| Facility | filters/30-by-facility.conf |
$syslogfacility |
local7 info-level messages |
| Severity | filters/40-by-severity.conf |
$syslogseverity |
All debug (severity 7) |
| Hostname | filters/50-by-hostname.conf |
$hostname/$fromhost-ip |
Nothing by default (examples only) |
Devices must send RFC 5424 structured-data syslog for msgid-based filters to work:
set system syslog host <collector-ip> any notice
set system syslog host <collector-ip> port 514
set system syslog host <collector-ip> source-address <loopback-ip>
set system syslog host <collector-ip> structured-data
- Add msgid filter: add a block to
filters/05-by-msgid.conf - Add daemon filter: add a block to
filters/10-by-programname.conf - Filter specific hosts: uncomment examples in
filters/50-by-hostname.conf - Test changes:
cd rsyslog && make testordocker compose run --rm test
See rsyslog/README.md for the full reference including deployment, PostgreSQL output setup, and Juniper configuration details.
cp .env.example .env
docker compose up -dThis starts TimescaleDB, the API, rsyslog, and the frontend.
| Service | Host Port | Variable |
|---|---|---|
| Frontend | 3000 | FRONTEND_HOST_PORT |
| API | 8080 | API_HOST_PORT |
| PostgreSQL | 5432 | POSTGRES_BIND |
| rsyslog (netlog) | 1514 | RSYSLOG_NETLOG_PORT |
| rsyslog (srvlog) | 1515 | RSYSLOG_SRVLOG_PORT |
docker compose exec api /app useradd --username admin --password adminDevelopment/demo tooling: the main binary ships three load generators (loadgen-netlog, loadgen-srvlog, loadgen-applog) for seeding a fresh instance with realistic events.
# Netlog events — network device logs (direct SQL insert)
docker compose exec api /app loadgen-netlog -n 100 --delay 100ms --jitter 200ms
# Netlog events via rsyslog (full pipeline)
docker compose exec api /app loadgen-netlog -n 100 --syslog rsyslog:514 --delay 100ms
# Srvlog events — server logs (direct SQL insert)
docker compose exec api /app loadgen-srvlog -n 100 --delay 100ms --jitter 200ms
# Srvlog events via rsyslog (full pipeline)
docker compose exec api /app loadgen-srvlog -n 100 --syslog rsyslog:514 --delay 100ms
# Application log events (HTTP ingest)
docker compose exec api /app loadgen-applog -n 100 --batch 1 \
--endpoint http://localhost:8080/api/v1/applog/ingesttaillight-shipper is a standalone binary that tails log files and pipes stdin, shipping structured log entries to the ingest API. Use it to onboard any application that writes to files or stdout.
pkg/logshipper is a Go slog.Handler — add it to your application to ship logs directly from code without an external process.
See cmd/taillight-shipper/ for configuration and usage.
The terminal UI client (taillight-tui) and the SSH server that hosts it
(taillight-wish) live in the companion repository
taillight-tui. Both are HTTP/SSE
clients of this API — see that repo for installation, configuration, and usage.
| Variable | Default | Description |
|---|---|---|
POSTGRES_PASSWORD |
taillight |
Database password |
POSTGRES_BIND |
127.0.0.1:5432 |
Bind address and port for PostgreSQL (host:port). Set to 0.0.0.0:5432 for remote access or change the port to avoid conflicts. |
API_HOST_PORT |
8080 |
Host port for the API |
RSYSLOG_NETLOG_PORT |
1514 |
Host port for netlog syslog input (514 in production) |
RSYSLOG_SRVLOG_PORT |
1515 |
Host port for srvlog syslog input (515 in production) |
FRONTEND_HOST_PORT |
3000 |
Host port for the web UI |
LOG_LEVEL |
info |
debug, info, warn, error |
AUTH_ENABLED |
false |
Enable authentication |
REAL_IP_HEADER |
(empty) | Trusted client-IP header set by your reverse proxy (e.g. X-Real-IP). Empty = trust only the TCP peer. Set it behind nginx or clients are attributed to the proxy IP. |
API_URL |
(empty) | Frontend API URL (empty = same-origin) |
CORS origins, connection pool sizes, retention policies, notification engine, SMTP, AI analysis, and Netbox enrichment. See config.yml.example for all options.
Environment variables always override config file values (Viper priority: defaults → config.yml → env vars).
API:
cd api
cp config.yml.example config.yml
make build && make test && make lintFrontend:
cd frontend
npm install && npm run devCLI commands:
| Command | Description |
|---|---|
serve |
Start the HTTP/SSE server |
migrate |
Run database migrations (up/down/version) |
loadgen-netlog |
Generate netlog test events (Juniper, Cisco, Arista) |
loadgen-srvlog |
Generate srvlog test events (Linux, Docker, PostgreSQL) |
loadgen-applog |
Generate application log test events |
useradd |
Create a user account |
apikey |
Manage API keys |
import |
Import Juniper netlog reference data (XLSX) |
version |
Print the build version |
Copy the XLSX files into the container, then run the import:
docker compose cp System_Log_Messages_Junos_OS_25.4R1.xlsx api:/tmp/
docker compose cp System_Log_Messages_Junos_OS_Evolved_25.4R1.xlsx api:/tmp/
docker compose exec api /app import --file /tmp/System_Log_Messages_Junos_OS_25.4R1.xlsx --os junos
docker compose exec api /app import --file /tmp/System_Log_Messages_Junos_OS_Evolved_25.4R1.xlsx --os junos-evolvedOr upload directly to a running server via the admin API. Requires an API key with the admin scope (apikey command) and accepts os=junos or os=junos-evolved:
curl -X POST Scan report · 2026-09-11
- ✓ Prohibited terms or links
- ✓ Repository eligibility
- ✓ slopscore.md paperwork
- ✓ Content policy
- ✓ Risk review





0 comments
log in to comment.