# `platform/observability/`

Logs + metrics + traces — wired to send everything to an OpenTelemetry Collector, then fan out to Prometheus (metrics) / Tempo (traces) / Loki (logs).

| File                                    | Role                                                                     |
| --------------------------------------- | ------------------------------------------------------------------------ |
| `otel-collector.yaml`                   | Receives OTLP, strips Authorization/Cookie headers, fans out             |
| `prometheus.yml`                        | Dev/static scrape config; prod uses prometheus-operator + ServiceMonitor |
| `loki-config.yml`                       | Single-binary Loki for local; prod uses distributed                      |
| `grafana/dashboards/http-latency.json`  | API p95 latency + error rate + RPS                                       |
| `grafana/dashboards/db-pool.json`       | Postgres connections + slow-query rate                                   |
| `grafana/dashboards/bullmq-queue.json`  | Queue depth + failure rate (audit + notifications)                       |
| `grafana/dashboards/business-kpis.json` | Active users + documents + pending approvals                             |

## App-side instrumentation

- **.NET API**: `OpenTelemetry.Extensions.Hosting` + `Instrumentation.AspNetCore/Http/EntityFrameworkCore`, exporter pointed at `OpenTelemetry__OtlpEndpoint` env var (Phase 4).
- **Node hosts**: pino logs include correlation-id; OTel SDK init lands as a worker-startup step (Phase 8+ refinement — currently logs only).
- **Correlation-Id** flows: ingress → app middleware → log MDC → audit event.

## Local stack

```bash
docker run -d --name otel-collector -p 4317:4317 -p 4318:4318 \
  -v $(pwd)/platform/observability/otel-collector.yaml:/etc/otel-collector.yaml \
  otel/opentelemetry-collector-contrib --config=/etc/otel-collector.yaml

docker run -d --name prometheus -p 9090:9090 \
  -v $(pwd)/platform/observability/prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus:latest

docker run -d --name loki -p 3100:3100 \
  -v $(pwd)/platform/observability/loki-config.yml:/etc/loki/local-config.yaml \
  grafana/loki:latest -config.file=/etc/loki/local-config.yaml

docker run -d --name grafana -p 3001:3000 \
  -e GF_AUTH_ANONYMOUS_ENABLED=true \
  -v $(pwd)/platform/observability/grafana/dashboards:/var/lib/grafana/dashboards \
  grafana/grafana:latest
```

Then add Prometheus + Loki datasources and import the dashboards from `/var/lib/grafana/dashboards`.

## SLOs

See [`docs/runbooks/slo.md`](../../docs/runbooks/slo.md) for the SLO + error-budget definitions wired to these dashboards.
