# Production observability wiring (Phase 36 Step 9) # # Local-dev Prometheus/Grafana/Seq remain in docker-compose.yml + infra/. # Production uses *existing external* instances — do not start those services # from docker-compose.prod.yml. ## Prometheus 1. Keep [infra/prometheus/prometheus.yml](../infra/prometheus/prometheus.yml) as the local-dev scrape of `host.docker.internal:5270`. Do not add production jobs there. 2. On the external Prometheus host, merge [infra/prometheus/production/scrape-vigilcare.yml](../infra/prometheus/production/scrape-vigilcare.yml) into `scrape_configs`. 3. Load [infra/prometheus/production/alert-rules.yml](../infra/prometheus/production/alert-rules.yml) via `rule_files`. 4. Optionally enable the commented blackbox probe for `/health/ready` (same scrape file). ### `/metrics` access The Clinical API uses a global `FallbackPolicy` that requires an authenticated user. `/metrics` is explicitly `.AllowAnonymous()` so Prometheus can scrape without a JWT. **Restrict at the network layer** — firewall so only the Prometheus host (and operators) can reach `API_PORT` /metrics. Metric labels include department and gateway identifiers. ## Grafana Prefer file provisioning over UI import (UI imports are lost on Grafana redeploy): 1. Copy datasources: [infra/grafana/production/datasources.yml](../infra/grafana/production/datasources.yml) — keep `uid: prometheus` so existing dashboards keep working. 2. Copy dashboards from [infra/grafana/dashboards/](../infra/grafana/dashboards/) to the path referenced by [infra/grafana/production/dashboards-provider.yml](../infra/grafana/production/dashboards-provider.yml). 3. Confirm panels render against the external Prometheus (job labels `vigilcare_api_prod` / `environment=production`). Minimum production alerts (also in Prometheus rules): API down, outbox backlog, Kafka consumer lag, ready probe failing, gateway offline, unacked CRITICAL alerts. ## Seq 1. Point production at Seq via env (compose already sets these): - `Seq__ServerUrl` / `Serilog__WriteTo__1__Args__serverUrl` → `SEQ_URL` - `Serilog__WriteTo__1__Args__apiKey` → `SEQ_API_KEY` (ingest-only key) 2. `appsettings.Production.json` sets `Microsoft.EntityFrameworkCore.Database.Command` to **Warning** so SQL with patient identifiers is not shipped to Seq. 3. Serilog `Properties:Application` is set to `VigilCareClinicalAPI` / `VigilCare.WardGateway` for filtering. 4. In Seq, create a signal (or shared dashboard) approximately: ``` Application = 'VigilCareClinicalAPI' and @Level in ['Error', 'Fatal'] ``` Optionally a second signal for the ward gateway with `Application = 'VigilCare.WardGateway'`.