Skip to content

Add observability: health checks, request IDs, and structured logging - #38

Merged
JFrusher merged 1 commit into
mainfrom
claude/compassionate-brown-2d2idn
Oct 1, 2026
Merged

JFrusher merged 1 commit into
mainfrom
claude/compassionate-brown-2d2idn

Conversation

@JFrusher

@JFrusher JFrusher commented Oct 1, 2026

Copy link
Copy Markdown
Owner

This PR adds comprehensive observability to the application through health checks, request tracing, and structured server-side logging.

Summary

Introduces a health check endpoint, request ID propagation across the entire request lifecycle, structured JSON logging with Pino, and build information tracking. These changes enable better monitoring, debugging, and tracing of requests through the system.

Key Changes

  • Health Check Endpoint (/api/health): New endpoint that returns deployment status (200 for healthy, 503 for degraded), build information (version, commit, timestamp, environment), and configuration status of optional services (database, accounts, error reporting)

  • Request ID Propagation: Every request receives a unique ID (generated or from caller's X-Request-ID/X-Correlation-ID headers) that is:

    • Validated to prevent log injection attacks
    • Forwarded through the middleware to all route handlers
    • Echoed back on every response
    • Attached to all log lines for request tracing
  • Structured Logging: Replaced console.error() calls with Pino-based structured JSON logging via requestLog(), which automatically includes the request ID and build information on every log line

  • Build Information:

    • Computed once at build time (buildInfo() in next.config.ts)
    • Inlined into all bundles via Next's env option
    • Includes version, commit hash, build timestamp, and environment
    • Exposed via X-App-Version and X-Commit-SHA response headers
    • Available in browser via window.appVersion
    • Sent to Sentry for error report correlation
  • Sentry Integration: Build information is now included in Sentry initialization for both server and browser, enabling better error tracking and correlation

  • Middleware Enhancements: Updated proxy.ts to handle request ID generation, validation, and propagation while maintaining existing sign-in redirect logic

Implementation Details

  • Request IDs are validated against /^[\w.:-]{1,128}$/ to prevent log injection
  • Build timestamp is fixed once per build so all processes agree
  • Health check performs a minimal database query (head-only, limit 1) to verify connectivity without fetching data
  • Logging respects LOG_LEVEL environment variable with sensible defaults (info in production, debug elsewhere)
  • All error handlers across API routes updated to use structured logging with request context

https://claude.ai/code/session_01VLGpqEpvfAjaAmtfcKdS9b

…i/health

Build metadata. next.config.ts computes the version (package.json), the short
commit (VERCEL_GIT_COMMIT_SHA, else git) and the build time once, and inlines
them through Next's `env` option, so the server, proxy and browser read the
same values via lib/build.ts. Every response carries X-App-Version and
X-Commit-SHA; window.appVersion holds the same in the browser.

Request ids. proxy.ts gives every request an X-Request-ID, keeping a caller's
X-Request-ID or X-Correlation-ID when it is a plain token of up to 128
characters. It forwards the id to route handlers and echoes it on the
response.

Structured logs. pino replaces console.error/info in every route handler and
in the proxy. Each line is JSON with the build, and with requestId when it is
written during a request. Messages are unchanged, so existing log searches
still match. LOG_LEVEL sets the level, defaulting to info in production and
debug elsewhere; it is read directly rather than through env(), so the log
works when the environment does not.

Errors. Both Sentry inits now carry a release and environment, plus
app_version and commit tags. Unhandled server errors are logged with their
request id and route template, and that id is tagged on the Sentry event.
Browser source maps ship with the deployment; the source is public AGPL, so
they reveal nothing new.

Health. GET /api/health returns status, build and per-service checks. The
database check is a head-only read of wedding_documents. It answers 503 when
a configured database does not respond; a backend-less deployment is
healthy.

Verified against `next start`: headers on pages and API routes, a log line's
requestId matching the X-Request-ID returned, a 503 with a database that does
not answer, one build timestamp in both server and client output, and every
loaded chunk's source map served.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLGpqEpvfAjaAmtfcKdS9b
Copilot AI balanced review requested due to automatic review settings October 1, 2026 07:23
@vercel

vercel Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
trousseau-suite Ready Ready Preview Oct 1, 2026 7:23am UTC

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation is coherent and tested; only a non-blocking documentation overstatement remains.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
What changed in this PR

Adds application observability through health monitoring, request correlation, build metadata, structured logging, and Sentry tagging.

Changes:

  • Adds /api/health with dependency and build status.
  • Propagates validated request IDs through middleware, responses, logs, and Sentry.
  • Replaces server console logging with Pino structured logs.
File Description
suite/​proxy.ts Generates and propagates request IDs.
suite/​proxy.test.ts Tests request ID behavior.
suite/​package.json Adds Pino.
suite/​next.config.ts Injects build metadata and response headers.
suite/​lib/​server/​log.ts Defines structured request logging.
suite/​lib/​server/​log.test.ts Tests request-bound log metadata.
suite/​lib/​sentry/​build.ts Defines shared Sentry build metadata.
suite/​lib/​headers.test.ts Tests build headers and timestamps.
suite/​lib/​build.ts Exposes inlined build information.
suite/​instrumentation.ts Adds server Sentry and unhandled-error correlation.
suite/​instrumentation-client.ts Exposes build information and configures browser Sentry.
suite/​app/​auth/​callback/​route.ts Adds structured error logging.
suite/​app/​api/​suppliers/​route.ts Adds request-aware logging.
suite/​app/​api/​suppliers/​[token]/​route.ts Adds request-aware logging.
suite/​app/​api/​share/​route.ts Adds request-aware logging.
suite/​app/​api/​share/​[token]/​route.ts Adds request-aware logging.
suite/​app/​api/​library/​route.ts Adds request-aware logging.
suite/​app/​api/​library/​[id]/​route.ts Adds request-aware logging.
suite/​app/​api/​health/​route.ts Implements the health endpoint.
suite/​app/​api/​health/​route.test.ts Tests health states and metadata.
suite/​app/​api/​documents/​route.ts Adds request-aware logging.
suite/​app/​api/​documents/​history/​route.ts Adds request-aware logging.
suite/​app/​api/​documents/​history/​[id]/​route.ts Adds request-aware logging.
suite/​app/​api/​documents/​export/​route.ts Adds request-aware logging.
suite/​app/​api/​cron/​sweep/​route.ts Structures retention sweep logs.
suite/​app/​api/​accounts/​weddings/​route.ts Adds request-aware logging.
suite/​app/​api/​accounts/​members/​route.ts Adds request-aware logging.
suite/​app/​api/​accounts/​invite/​route.ts Adds request-aware logging.
suite/​app/​api/​accounts/​invite/​[token]/​route.ts Adds request-aware logging.
suite/​app/​api/​accounts/​delete/​route.ts Adds request-aware logging.
suite/​app/​api/​accounts/​delete/​route.test.ts Updates tests for request parameters.
suite/​.env.example Documents LOG_LEVEL.
package-lock.json Locks Pino dependencies.
docs/​SELF-HOSTING.md Documents health checks and observability.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/SELF-HOSTING.md
Comment on lines +191 to +192
Every response carries `X-App-Version`, `X-Commit-SHA` and `X-Request-ID`. In
a browser console, `window.appVersion` says the same. The server logs one JSON
@JFrusher
JFrusher merged commit 20aefa8 into main Oct 1, 2026
9 of 11 checks passed
@JFrusher
JFrusher deleted the claude/compassionate-brown-2d2idn branch October 1, 2026 07:27

This branch was successfully deployed

1 active deployment
Preview — fe19730c Deployed Oct 1, 2026 by vercel[bot]
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.

3 participants