Skip to content

Expose metrics and a health check - #64

Merged
jbpratt merged 2 commits into
masterfrom
feat/metrics-and-health
Aug 16, 2026
Merged

Expose metrics and a health check#64
jbpratt merged 2 commits into
masterfrom
feat/metrics-and-health

Conversation

@jbpratt

@jbpratt jbpratt commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

modbot had no way to answer "is it alive right now" other than typing in chat and waiting. This adds /metrics and /healthz on :9090, a keepalive PING to drive connection state, and a container HEALTHCHECK so a wedged bot gets restarted instead of sitting there.

Four metrics, deliberately:

Metric Why
modbot_connected the one that matters — ws death, expired cookie, crash-loops
modbot_last_message_received_timestamp_seconds chat traffic, informational only
modbot_ws_reconnects_total climbing = dial succeeds but session rejected, usually the cookie
modbot_command_persist_failures_total an !addcommand acknowledged and then lost

Plus go_* / process_* free from client_golang. Nothing is labelled by nick — unbounded cardinality, and it would turn the metrics store into a record of user behaviour.

Health is the socket being up, not chat being busy

Gating on time-since-last-message would restart the bot for being quiet at 4am. SendPing fails when the socket is gone, so a ping write landing is the signal. The PONG is never read, which also sidesteps dggchat's parsePing base64-decoding the payload and listen() silently skipping the handler when that fails.

An expired cookie is still caught: the server closes the socket, the read errors, onSocketError fires, modbot_connected drops and ws_reconnects climbs.

Notes

  • The image is FROM scratch, so HEALTHCHECK runs the binary's own -healthcheck mode. Verified baked into the image as ["CMD","/modbot","-healthcheck"].
  • No ports are published. Scraping happens by container name over the existing strims docker network, so hooks/redeploy needs no change.
  • Image grows 9.9 MB -> 16.5 MB from client_golang.
  • Nothing scrapes this yet. Merging changes nothing observable except that docker ps starts showing a health status. The collector stack is the next piece of work.

⚠️ modbot-cd.yaml deploys on push to master, so merging this ships it.

jbpratt and others added 2 commits August 16, 2026 10:46
modbot had no way to answer "is it alive right now" other than typing in
chat and waiting. Add /metrics and /healthz on :9090, a keepalive PING to
drive connection state, and a container HEALTHCHECK so a wedged bot gets
restarted instead of sitting there.

Health is the websocket being up, not chat being busy. Gating on time since
the last message would restart the bot for being quiet at 4am. SendPing
fails when the socket is gone, so a ping write landing is the signal; the
PONG is never read, which also sidesteps dggchat's parsePing base64-decoding
the payload and listen() silently skipping the handler when that fails.

An expired cookie is still caught: the server closes the socket, the read
errors, onSocketError fires, modbot_connected drops and ws_reconnects climbs.

Four series, deliberately: connected, last_message_received, ws_reconnects,
command_persist_failures. Nothing labelled by nick -- unbounded cardinality,
and it would make the metrics store a record of user behaviour.

The scratch image has no shell or curl, so HEALTHCHECK runs the binary's own
-healthcheck mode.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docker records the check command's output in .State.Health.Log, so the
reason is worth passing along -- "healthz 503 Service Unavailable: websocket
down" beats a bare status code when you are reading docker inspect at 3am.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jbpratt
jbpratt merged commit d8516d0 into master Aug 16, 2026
1 check 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