Skip to content

Head-to-head races, logs, traces and eBPF request metrics - #1

Merged
rshdhere merged 3 commits into
mainfrom
feat/o11y-logs-traces-races
Oct 4, 2026
Merged

rshdhere merged 3 commits into
mainfrom
feat/o11y-logs-traces-races

Conversation

@rshdhere

@rshdhere rshdhere commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

What this adds

  • Head-to-head races (/run/compare): 2-3 servers get the same rate at the same moment; new Head to Head dashboard with winners, scoreboard and overlaid graphs.
  • Logs: Alloy ships this project's container logs (and nothing else on the machine) to Loki, 7 days kept.
  • Traces: Tempo 3.1 receives Beyla's sampled server spans and the runner's OpenTelemetry spans (one span per visitor request and per load test), 3 days kept.
  • Metrics from inside the servers: Beyla (eBPF, no code changes, all 13 servers in 4 languages) records rate, errors and duration for every request. Observe-only: context propagation and the Node.js inspector injection are disabled.
  • Linked signals: runner logs carry trace_id (link to the trace), spans link to their service's logs and request metrics.
  • Dashboards: new Inside the Servers; per-server eBPF, logs and traces rows; server-side p99 on Live and Head to Head; pipeline and runner rows on Observability Stack.
  • Alerts: optional chat notifications from ALERT_WEBHOOK_URL in infra/.env (load-test alerts stay dashboard-only), alert lists on dashboards, and a server-side 5xx alert.
  • Fixes: Grafana's router turned /run/<server> links into its 404 page; k6 rate/match tags were dropped because CLI --tag replaces options.tags; FastAPI no longer writes an access log per request (Flask and Django already did not).

Behaviour changes to know before merging

  • Each server now listens on its servers.json port inside its container (Fiber on 3106 everywhere), so Beyla can match servers by port.
  • Beyla runs privileged with the host PID namespace and needs a kernel with BTF (the VPS has 6.8).
  • New containers with memory caps: Loki 384 MB, Tempo 512 MB, Alloy 256 MB, Beyla 384 MB (the VPS has 4 GB, about 1.9 GB in use).
  • Each deploy now restarts the runner (its commit changes), which cuts off a visitor test running at that moment.

Testing

  • Full stack run locally on Docker Engine with real races: Beyla instrumented all 13 servers and counted ~500 req/s per racer, Tempo received traces from every server and the runner, runner log lines carry the trace ID of their load-test span, and Grafana 13.2.3 rendered the new panels.
  • Notification provisioning tested in both modes against Grafana 13.2.3 (an invalid weekday range that stopped Grafana from starting was caught and fixed).
  • deploy.sh's config-change restart verified against running containers; every CI check passes locally (generator, gofmt/vet/build, typecheck, compose, promtool, Loki, Alloy, Tempo, nginx, shellcheck).

After merging (on the VPS)

  • sudo ufw allow from 172.30.0.0/24 to 172.30.0.1 port 9100 proto tcp comment 'prometheus -> node-exporter' so the Host dashboard gets data.
  • Optional: set ALERT_WEBHOOK_TYPE / ALERT_WEBHOOK_URL in infra/.env for chat notifications.

…rics

- Add bench/k6/src/match.ts and runner /run/compare for 2-3 server races, plus a Head to Head dashboard
- Add Loki + Alloy (container logs), Tempo 3 (traces) and Beyla (eBPF RED metrics and sampled traces for all 13 servers, observe-only)
- Give each server its own internal port from servers.json so Beyla's generated config instruments only them
- Instrument the runner with OpenTelemetry spans and slog JSON logs carrying trace IDs; link Loki, Tempo and Prometheus datasources
- Add Inside the Servers dashboard and eBPF, logs and traces rows on server, Live, Head to Head and Stack dashboards
- Fix runner links in Grafana (target=_self / new tab) that Grafana's router turned into a 404
- Add optional webhook alert notifications via deploy.sh, alert lists on dashboards, and a server 5xx alert
- Move k6 rate/match tags onto scenarios, since CLI --tag replaces options.tags
- Disable uvicorn access logs in FastAPI to match Flask and Django
…hem on deploy

- Validate Loki (-verify-config), Alloy (validate) and Tempo (-config.verify) configs with their own images in the checks job
- Fail the checks job when infra/beyla/beyla.yaml drifts from generate.py or the runner is not gofmt-clean
- Reload Alloy on SIGHUP alongside Prometheus and blackbox in infra/deploy.sh
- Restart Loki, Tempo or Beyla only when their config file changed since the container started
- Fail the deploy unless Grafana reaches the Loki and Tempo datasources
…ces views

- Give every timeseries that reads Beyla metrics a 15s minimum interval, since the 1s datasource interval made $__rate_interval shorter than Beyla's 5s scrape
- Set Beyla duration buckets from 0.1 ms upward so sub-millisecond server latency gets real p50/p99 instead of the 5 ms default bucket
- Limit Slowest recent requests to the 13 servers and a 5 ms threshold, so the runner's 30s load-test traces no longer fill it
- Make All on Inside the Servers mean the servers and the runner, keeping the stack's own logs opt-in
- Sum Prometheus's samples-appended counter, which Prometheus 3 splits by sample type
- Document Beyla's kernel and privilege needs, memory caps, retention, the new CI checks and deploy reloads
@rshdhere
rshdhere merged commit d818af8 into main Oct 4, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant