Skip to main content

Observability

Structured logs

Background work logs a one-line, prefixed summary per run so a wrangler tail is readable at a glance.

The Cron handler (api/src/scheduled.ts) logs each job's outcome and swallows its errors so one bad run never crashes the schedule:

[cron] demo generator added 3 responses
[cron] alert escalation: 2 reminded, 1 escalated, 3 emails sent
[cron] export schedules: 4 due, 4 queued
[cron] expired 7 stale partial response(s)

The queue consumer (api/src/queue.ts) logs a failure-and-retry line per bad message ([queue:<name>] message failed, retrying), and the producer (api/src/ingest.ts) logs [ingest] enqueue failed on a transient publish failure — both keep the request path unaffected (see Queues).

Health & readiness endpoints

The Worker exposes two probes (api/src/app.ts):

  • GET /health — liveness. Returns a static { ok, service, version, time }, validated against the healthResponse contract. version is SERVICE_VERSION. It constructs no providers, so it's cheap and always 200 if the Worker is up.
  • GET /health/ready — deep readiness. Pings the database (ports.db.ping()) and returns { ok: true, db: 'up' } on success or 503 { ok: false, db: 'down' } on failure. It fails closed so a load balancer / probe pulls the instance when the DB (Hyperdrive → Neon) is unreachable, rather than serving errors.

Smoke-test both after a deploy:

curl -fsS https://<worker-domain>/health
curl -fsS https://<worker-domain>/health/ready

A 200 on /health/ready confirms the Hyperdrive binding + migrations are good; a non-200 means the Worker is up but the DB path is misconfigured.

Error handling envelope

Unhandled errors are normalised by the Hono error handler into a consistent envelope rather than a raw stack:

  • Zod validation errors → 400 { error: { code: 'validation_error', … } }.
  • A malformed UUID reaching a uuid column (Postgres 22P02) → 404 not_found.
  • Anything else → console.error('unhandled error', err) then 500 internal.

The SPA's API client unwraps this envelope to surface the server's message (see SPA overview).

Sentry

The API Worker is wrapped with @sentry/cloudflare. When SENTRY_DSN is configured, Hono captures unclassified errors and the wrapper captures exceptions escaping fetch, Queue or Cron entrypoints; with no DSN it is a safe no-op. Default PII and performance tracing are disabled. The browser has no Sentry SDK today, so VITE_SENTRY_DSN is reserved configuration rather than an active integration.