> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Developer Guide: Monitoring & Error Handling

> — Error Handling & Monitoring Last Updated: 2026-03-14

**Spec:** PF-07 — Error Handling & Monitoring\
**Last Updated:** 2026-03-14

> For operational procedures (alerting, dashboards, escalation), see [MONITORING\_RUNBOOK.md](../operations/MONITORING_RUNBOOK.md).

***

## Structured Logger

All logging must use the platform `logger`. **Never use `console.log`** in production code.

```typescript theme={null}
import { logger } from '@/platform/monitoring';

// Info-level log with context
logger.info('Employee created', {
  module: 'hr',
  action: 'employee_create',
});

// Error-level log with error object
logger.error('Failed to save document', error, {
  module: 'pf',
  action: 'document_save_error',
});

// Set persistent context (e.g., after login)
logger.setContext({
  user_id: session.user.id,
  org_id: organizationId,
  correlation_id: crypto.randomUUID(),
});

// Clear context on logout
logger.clearContext();
```

### Rules

* **NEVER log PHI/PII** — only stable IDs (user\_id, org\_id, entity UUIDs)
* Always include `module` (core prefix: `pf`, `hr`, `fa`, etc.) and `action`
* Use `logger.error()` with an `Error` object for stack trace capture
* Use `sanitizeErrorMessage(error)` for any user-facing error text

***

## Error Boundaries

Wrap components in `<ErrorBoundary>` to prevent crashes from propagating.

```typescript theme={null}
import { ErrorBoundary } from '@/platform/monitoring';

// Feature-level boundary (recommended for route modules)
<ErrorBoundary level="feature">
  <MyFeatureModule />
</ErrorBoundary>

// Component-level boundary (for non-critical widgets)
<ErrorBoundary level="component" fallback={<p>Widget unavailable</p>}>
  <OptionalWidget />
</ErrorBoundary>
```

### Levels

| Level       | Use Case                          | Fallback                          |
| ----------- | --------------------------------- | --------------------------------- |
| `global`    | App root (`main.tsx`, `App.tsx`)  | Full-page error screen            |
| `feature`   | Route modules (`RouteLoader.tsx`) | Module-level error screen         |
| `component` | Individual widgets                | Custom or default inline fallback |

### Suppressed Errors

The ErrorBoundary suppresses known non-actionable errors (WebSocket unavailable, insecure context) to prevent unnecessary UI crashes. See `SUPPRESSED_PATTERNS` in `ErrorBoundary.tsx`.

***

## Performance Monitoring

### Automatic Tracking

* **Web Vitals** (LCP, INP, CLS): Tracked by Sentry via `reactRouterV6BrowserTracingIntegration`
* **Page load / DOM ready**: Tracked by `performanceMonitor` → `pf_health_metrics`

### Custom Marks

```typescript theme={null}
import { performanceMonitor } from '@/platform/monitoring';

// Measure a specific operation
const markId = performanceMonitor.startMark('heavy-calculation');
// ... do work ...
performanceMonitor.endMark(markId);
```

### API Metrics

API response times are tracked automatically via `api-metrics.ts` when enabled. Histograms are flushed to `pf_health_metrics` periodically.

***

## Sentry Integration

### Error Capture

Sentry captures unhandled errors automatically. For manual capture:

```typescript theme={null}
import * as Sentry from '@sentry/react';

// Capture an exception with context
Sentry.captureException(error, {
  tags: { module: 'hr', action: 'payroll_calc' },
});

// Capture a message
Sentry.captureMessage('Unexpected state', 'warning');
```

### Session Replay

Session replays are recorded at 100% for error sessions. All text is masked and media is blocked for HIPAA compliance. No configuration needed — this is handled in `sentry.ts`.

### PHI Safety

The `beforeSend` callback in `sentry.ts` scrubs potential PHI patterns (emails, phone numbers, SSNs, DOBs) from all events before they leave the browser. Only UUIDs are sent as user/org identifiers.

***

## Correlation IDs

Auth state changes generate a `correlation_id` (via `crypto.randomUUID()`) that is set in the logger context. This enables tracing all logs from a single auth session.

The correlation ID is automatically set in `App.tsx` for `SIGNED_IN`, `TOKEN_REFRESHED`, and `SIGNED_OUT` events.

***

## Checklist for New Features

* [ ] Use `logger` instead of `console.*`
* [ ] Include `module` and `action` in all log context
* [ ] Never log PHI/PII
* [ ] Wrap route modules in `<ErrorBoundary level="feature">`
* [ ] Use `sanitizeErrorMessage()` for user-facing errors
* [ ] Add `performanceMonitor.startMark()`/`endMark()` for heavy operations
