Log hygiene: Alloy CPU, Loki chunks/limits, Caddy field-stripping #36
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Jinemi/thermograph#36
Loading…
Reference in a new issue
No description provided.
Delete branch "infra/logging-optimizations"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What changed
Alloy (
observability/alloy/config.alloy):file_watchpoll interval 250ms/250ms -> 2s/10s on bothloki.source.fileblocks;local.file_matchsync_period10s -> 1m.discovery.dockerrefresh_interval60s -> 5m (Swarm task churn was restarting tailers ~every 90s).alloy,autoscaler,thermograph-test_*,thermograph-lb,thermograph_worker.loki.source.dockerdouble-relabeling (it took both the pre-relabelled target output andrelabel_rules, running every rule twice and making the new drop rules a no-op on the second pass).loki.process "caddy"stage that drops known crawler/bot lines (semrushbot, claudebot, ahrefsbot, yandexbot, bytespider, mj12bot, petalbot) -- the Caddy-sidelog_skipneeds >= 2.7 and both hosts are older.Loki (
observability/loki/config.yml):chunk_idle_period30m -> 2h,max_chunk_age12h, explicitchunk_target_size/chunk_encoding; explicit ingestion/stream limits (ingestion_rate_mb,per_stream_rate_limit,max_global_streams_per_user, etc.) sized for a 3-node hobby fleet -- their absence was the source of intermittent 429s.CI (
.forgejo/workflows/observability-validate.yml): added real syntax coverage foralloy/config.alloy-- downloads the pinned v1.9.1alloybinary from its GitHub release and runsalloy validate(there was none before).Caddy (
infra/terraform/modules/thermograph-host/templates/Caddyfile.tftpl+infra/deploy/Caddyfile):health_interval5s -> 15s; afilterformat log encoder droppingrequest>headers,request>tls,resp_headers(the main driver of ~1,133B/line on prod, ~922B on beta) plusroll_size/roll_keep; and aregexpfield filter that strips the query string entirely from logged request URIs (Caddy's default logger recorded it verbatim -- a real privacy leak, since every?q=<search text>sat in Loki next to the client IP for 30 days).Where the true source of truth was found
CUTOVER-NOTES.md) has landed. It can still be cloned/viewed but not pushed to, so this domain's work lands here inobservability/instead of a standalone-repo PR as originally planned.docker inspecton prod that the livealloycontainer's config bind-mount source is/opt/observability/alloy/config.alloy-- a checkout of the now-archived split repo, not this monorepo'sobservability/copy. That means this PR fixes the canonical/going-forward copy, but taking effect on hosts needs a follow-up cutover of the observability stack's own deploy (analogous to the already-completed app-side/opt/thermographswap) -- flagged, not done here, since it's a host-side change outside this PR's scope./etc/caddy/Caddyfile's own header says "RENDERED BY TERRAFORM for<domain>", matchingCaddyfile.tftpl's template header -- that's the true source, not a hand-edited file.infra/deploy/Caddyfileis a documented manual-apply fallback (seeinfra/DEPLOY.md) and was updated in step to avoid the two drifting further.Validated
alloy validate(real v1.9.1 binary, downloaded from the GitHub release) against the edited config: passes. (One pre-existing, unrelated tool gap:alloy validatedoesn't recognize the top-levellivedebugging {}singleton block as a component, even thoughalloy runloads it fine -- confirmed this is not something my change introduced by validating the original file too. Worked around in both the local check and the new CI step by stripping that one line before validating.)loki -verify-config(real v3.5.1 image, matching what's deployed) against the edited config: passes.caddy validateagainst the rendered template (viaterraform console+ the realcaddybinary on beta, matching what's actually installed there) and against the editedinfra/deploy/Caddyfile: both pass.caddy runon beta (a temporary process on an unused port, not the real service) -- confirmed theurifield in the resulting JSON log line drops the query string entirely, and confirmed empirically that keepingrequest>headers>User-Agentalongside a parent-levelrequest>headers deleteis not achievable on beta's actual Caddy version (2.6.2): deleting the parent drops the whole subtree regardless of a child override, and thelog_appenddirective that would let User-Agent be hoisted out first isn't available before Caddy 2.8ish. Traded away rather than block on it -- noted in the code comment.Host-level changes applied directly (not in any repo, per scope)
/etc/docker/daemon.json({"log-driver": "json-file", "log-opts": {"max-size": "10m", "max-file": "3"}}) written on both prod and beta. Not restarting dockerd on either -- checked first: prod is the fleet's sole Swarm manager (Managers: 1, this is the Leader) with Live Restore disabled, so a daemon restart there would both interrupt Swarm's control plane and actually stop/restart every container on the host (no live-restore to keep them running) -- a real outage window, not a blip. Deferring both restarts to a planned maintenance window; flagging this explicitly rather than forcing it./etc/cron.d/thermograph-applogs-retentionon both hosts:find /var/lib/docker/volumes/thermograph_applogs/_data -name '*.jsonl' -mtime +14 -delete, daily at 03:00. Used plain cron rather than logrotate (logrotate is set up and active on both hosts for other logs, but the app already self-dates one file per category per day -- e.g.access-2026-07-24.jsonl-- so logrotate's rotate-in-place model doesn't fit files it doesn't own the naming of; a straightfind -deleteis the correct tool here).docker service lsreplicas 1/1 on prod, andthermograph.org,beta.thermograph.org,git.thermograph.orgall return 200,dashboard.thermograph.orgits normal 302 (Google OAuth redirect) -- nothing was disrupted.Grafana
Not touched. No changes to
observability/docker-compose.yml,observability/grafana/,observability/caddy-grafana.conf, ordashboard.thermograph.org's Caddy block. Confirmed viagit diff --statthat only the five files listed above changed.Deferred / flagged
/opt/observabilityfrom the archived split repo to track this monorepo'sobservability/subtree) -- needed before any of the Alloy/Loki changes here actually take effect in production. Out of scope for this PR.