# Error Monitoring & Logging ## Overview The WorkRoot site uses a two-layer monitoring approach: 1. **Structured Logger** (`src/utils/logger.ts`) — always active, zero dependencies 2. **Sentry Integration** (`src/utils/sentry.ts`) — optional, activated via `SENTRY_DSN` env var --- ## Files Created / Modified | File | Change | |------|--------| | `src/utils/logger.ts` | **New** — structured logger | | `src/utils/sentry.ts` | **New** — Sentry HTTP integration | | `src/middleware.ts` | **Updated** — logs all requests + SSR errors | | `src/pages/api/contact.ts` | **Updated** — structured logs + Sentry on email failure | | `src/pages/api/newsletter.ts` | **Updated** — structured logs + Sentry on SMTP failure | | `.env.example` | **Updated** — added `SENTRY_DSN`, `LOG_LEVEL`, `RELEASE_VERSION` | --- ## Structured Logger ### Usage ```typescript import { logger, logApiRequest } from '../../utils/logger'; // General logging logger.debug('scope', 'message', { key: 'value' }); logger.info('scope', 'message', { key: 'value' }); logger.warn('scope', 'message', { key: 'value' }); logger.error('scope', 'message', { key: 'value' }); // API request logging logApiRequest({ scope: 'api.contact', method: 'POST', path: '/api/contact', status: 200, ip: clientAddress, durationMs: Date.now() - startTime, meta: { subject: formData.subject }, }); ``` ### Output Format **Development** — human-readable with color: ``` [INFO] 2026-03-21T08:00:00.000Z [api.contact] POST /api/contact 200 {"durationMs":42} [ERROR] 2026-03-21T08:00:01.000Z [api.contact] Email delivery failed {"error":"..."} ``` **Production** — JSON for log aggregation: ```json {"level":"info","scope":"api.contact","message":"POST /api/contact 200","timestamp":"2026-03-21T08:00:00.000Z","durationMs":42} ``` ### Log Levels | Level | When | |-------|------| | `debug` | Verbose request tracing (dev only by default) | | `info` | Successful operations, informational events | | `warn` | Rate-limit hits, 4xx errors, degraded operations | | `error` | 5xx errors, email failures, unhandled exceptions | Configure minimum log level via `LOG_LEVEL` env var (`debug`/`info`/`warn`/`error`). --- ## Sentry Integration ### Setup 1. Create a project at [sentry.io](https://sentry.io) (Node.js platform) 2. Copy the DSN from **Settings → Client Keys (DSN)** 3. Add to your environment: ```env SENTRY_DSN=https://xxx@oXXXXXX.ingest.sentry.io/XXXXXXX RELEASE_VERSION=1.0.0 ``` No SDK installation required — the integration uses Sentry's HTTP store API directly. ### Usage ```typescript import { captureException, captureMessage } from '../../utils/sentry'; // Capture an exception (also logs via structured logger) await captureException(error, { scope: 'api.contact', user: { ip: clientAddress }, extra: { email: formData.email }, tags: { feature: 'contact-form' }, }); // Capture a message await captureMessage('Rate limit exceeded', 'warning', { scope: 'api.contact', extra: { ip }, }); ``` ### What Gets Captured | Event | Sentry Level | Location | |-------|-------------|----------| | SSR middleware crash | error | `src/middleware.ts` | | Contact form email failure | error | `src/pages/api/contact.ts` | | Newsletter SMTP failure | error | `src/pages/api/newsletter.ts` | ### Without SENTRY_DSN All calls to `captureException` / `captureMessage` fall back to structured logger only. **No errors are swallowed** — monitoring degrades gracefully. --- ## Middleware Request Logging All HTTP requests pass through `src/middleware.ts`, which now logs: - **5xx** → `error` level (also tries to capture in Sentry) - **4xx** → `warn` level - **All others** → `debug` level (dev only) Duration is measured from request start to response completion. --- ## Environment Variables ```env # Required for Sentry error tracking SENTRY_DSN=https://xxx@oXXXXXX.ingest.sentry.io/XXXXXXX # Optional — minimum log level (default: info in prod, debug in dev) LOG_LEVEL=info # Optional — used for Sentry release tracking RELEASE_VERSION=1.0.0 ``` --- ## Upgrading to Full Sentry SDK For richer features (performance monitoring, session replay, source maps): ```bash npm install @sentry/node ``` Then replace `src/utils/sentry.ts` with: ```typescript import * as Sentry from '@sentry/node'; Sentry.init({ dsn: import.meta.env.SENTRY_DSN, environment: import.meta.env.PROD ? 'production' : 'development', release: process.env.RELEASE_VERSION, tracesSampleRate: 0.1, // 10% of transactions }); export const captureException = (err: unknown, ctx?: object) => Sentry.captureException(err, ctx ? { extra: ctx as Record } : undefined); export const captureMessage = (msg: string, level = 'info') => Sentry.captureMessage(msg, level as Sentry.SeverityLevel); ``` --- ## Log Aggregation (Production) Since production output is newline-delimited JSON, it integrates with: - **Logtail / Better Stack** — point log drain at your server stdout - **Datadog** — use their Node.js log agent - **Render / Railway / Fly.io** — all capture stdout automatically; pipe to their log service - **Self-hosted** — any log shipper that reads Docker/PM2 stdout (Filebeat, Promtail, etc.) --- ## Testing Verify structured logging is working: ```bash # Development — see colored output npm run dev # Visit http://localhost:4321/api/health → should see DEBUG log # Production — see JSON output NODE_ENV=production node server.mjs ``` Verify Sentry is working: ```bash # Set SENTRY_DSN in .env, then trigger an error: curl -X POST http://localhost:4321/api/contact \ -H "Content-Type: application/json" \ -d '{"name":"Test","email":"bad@","subject":"other","message":"test"}' # Check your Sentry dashboard for the captured event ```