# ADR 016: Logging and Observability Facade Strategy

**Status**: Accepted (facade in Phase 1; OTel integration in Phase 3; vendor exporters Phase 9)
**Date**: 2026-04-19
**Deciders**: Architecture Lead, SRE

---

## Context

Today `lib/logger.ts` exposes a single `logError` helper that calls `console.error` in development and silently drops in production. Direct `console.*` is sprinkled across `app/` and `lib/`. BMW's Dynatrace integration plus general operability require structured, leveled, correlation-ready logging that is **vendor-agnostic** in the codebase and **vendor-specific** only in the exporter.

## Decision

Replace direct `console.*` usage with a typed logger facade that emits OpenTelemetry-shaped structured records:

```ts
{ timestamp, level, message, ...context, trace_id?, span_id?,
  tenant_id?, user_id?, request_id?, service, env, release }
```

Phase 1 deliverable (this repo):

- `lib/logger.ts` becomes the facade with leveled methods (`debug`, `info`, `warn`, `error`).
- `child()` returns a logger with bound context (e.g. request id, tenant id).
- Redaction list of known sensitive keys (`password`, `token`, `service_role_key`, `cookie`, `authorization`, `set-cookie`).
- Default sink writes JSON to stdout in production and pretty text in development.
- `logError(context, err)` retained as a backwards-compatible shim.

Phase 3: real OTel SDK with OTLP exporter.
Phase 9: BMW profile adds Dynatrace OneAgent exporter alongside OTLP. No changes in calling code.

## Rules this decision creates

1. New code uses `import { logger } from "@/lib/logger"`; `console.*` is forbidden in non-test code (Phase 1 warning, Phase 2 error).
2. Sensitive fields named in the redaction list are scrubbed before serialization. Adding a sensitive field name is a one-line update to the facade.
3. Loggers carrying request scope (tenant id, request id) are created via `logger.child({...})`; do not mutate global state to attach context.
4. Vendor SDKs (Dynatrace, Datadog, Sentry) are referenced only inside `lib/customers/<customer>/` adapters or `lib/observability/exporters/`, never inside core call sites.

## Consequences

### Forbidden

- `console.log`, `console.error`, `console.warn` in non-test source files (Phase 1 advisory; Phase 2 lint-enforced).
- Importing a vendor observability SDK from feature/module code.
- Logging raw request bodies, full cookies, or full auth headers.

### Accepted

- Migration of existing `console.*` happens incrementally. Locations not migrated in Phase 1 carry a `// TODO(logger-migration)` marker with the date.

## Related

- ADR 014 — Shared platform services strategy
- ADR 017 — Security baseline and scan policy
- ADR 018 — CSP enforcement strategy
