Skip to content

Logs & Monitoring

Where logs come from

Source Format Where
Hono API JSON-ish lines via hono/logger stdout
WS server Plain text stdout
Next.js Next's default stdout
Docore agent / sandbox audit Structured [audit], [docore] prefix stdout
PostgreSQL Postgres log format varies
Caddy / nginx Access + error logs /var/log/caddy/, /var/log/nginx/
Cloudflare Tunnel Plain text journalctl

Reading logs by deployment mode

Docker

docker compose -f docker/docker-compose.yml logs -f               # all
docker compose -f docker/docker-compose.yml logs -f api ws web    # filtered
docker compose -f docker/docker-compose.yml logs -f --tail 500 api

For long-term storage, run a logging driver:

logging:
  driver: json-file
  options:
    max-size: "10m"
    max-file: "5"

…or ship to Loki / CloudWatch via the loki / awslogs drivers.

Bare-metal (systemd + tmux)

journalctl -u doable -f                  # the systemd unit (wraps tmux)
journalctl -u doable -e -n 1000          # last 1000 lines
journalctl -u cloudflared -f
sudo tail -f /var/log/caddy/access.log
sudo tail -f /var/log/caddy/error.log

If you want per-window logs (api / web / ws), tmux pipe-pane redirects each to a file:

tmux pipe-pane -t doable:api 'cat >>/var/log/doable/api.log'
tmux pipe-pane -t doable:web 'cat >>/var/log/doable/web.log'
tmux pipe-pane -t doable:ws  'cat >>/var/log/doable/ws.log'

Useful greps

# Auth failures
journalctl -u doable | grep -E '401|invalid_token'

# Sandbox denies
journalctl -u doable | grep '[audit] deny'

# AI provider errors
journalctl -u doable | grep -E 'anthropic|openai|copilot' | grep -i error

# Slow requests (> 1s)
journalctl -u doable | grep 'Server-Timing' | grep -E '\b[0-9]{4,}ms\b'

Health checks

curl https://<host>/api/health
# {"status":"ok","db":"ok","ws":"ok","uptime":1234}

Wire this up to your uptime monitor (UptimeRobot, BetterUptime, Pingdom) β€” alert on non-200 or missing db: ok.

Metrics

Doable doesn't ship a Prometheus endpoint by default. The Tracer from @doable/docore lets you hook OpenTelemetry exporters:

import { Tracer } from "@doable/docore";

const tracer = new Tracer({
  sink: (span) => otelExporter.export(span),
});

Pass to DoCoreEngine. Spans cover session lifecycle, tool calls, and provider request timing.

For HTTP metrics, Hono's timing middleware emits Server-Timing headers β€” scrape from your edge proxy.

Suggested alert thresholds

Signal Threshold Reason
5xx rate > 1% over 5 min Real user impact
Auth-fail rate > 10/sec Brute-force or credential stuffing
Sandbox-deny rate > 1/sec Possibly compromised account
AI provider error rate > 5% Quota exhaustion or upstream outage
Disk usage on PROJECTS_ROOT > 80% Will affect new project creation
DB connections > 80% of pool Pool sized too small or query leak
WS connection drops > 10/sec Network issue or service crash

Rotation

For bare-metal:

/var/log/doable/*.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
    copytruncate
}

Drop in /etc/logrotate.d/doable.

For Docker, the json-file driver with max-size / max-file does the rotation in-place.