# 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<string, unknown> } : 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
```
