feat: add end-to-end observability (metrics, health, mobile RUM, dashboards, alerts)
Run Tests on Branches / Detect Changes (push) Successful in 10s
Run Tests on Branches / Backend Tests (push) Failing after 20s
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Failing after 20s

- Backend: OTel metrics via OTLP -> Alloy -> Prometheus (OTEL_METRICS_EXPORTER=otlp)
- New business metrics (src/metrics.js): searches, CIMA latency/errors, cache
  hits/misses, logins, rate-limits, pharmacy writes/links, push sent/failed,
  DB + Redis timings/errors, HTTP req count/duration, heartbeat
- Backend health endpoints /healthz and /readyz
- Mobile (Expo): Grafana Faro RUM via @grafana/faro-react-native
- redis/postgres exporters in docker-compose + Prometheus scrape jobs
- Grafana dashboards (backend, datastores, mobile RUM, overview)
- Prometheus alert rules (farmafinder_*) -> existing Alertmanager (Telegram)
- Design/spec saved to docs/superpowers/specs/
This commit is contained in:
Antoni Nuñez Romeu
2026-07-13 15:57:52 +02:00
parent 2713be85a3
commit 839c64c12a
16 changed files with 907 additions and 27 deletions
@@ -0,0 +1,58 @@
{
"annotations": { "list": [] },
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"id": 1, "title": "Events/sec", "type": "timeseries",
"gridPos": { "h": 6, "w": 8, "x": 0, "y": 0 },
"datasource": { "type": "loki", "uid": "bfowk7qjoccu8b" },
"targets": [ { "expr": "sum(count_over_time({app=\"farmafinder-mobile\"}[5m]))", "legendFormat": "events/s" } ]
},
{
"id": 2, "title": "Active sessions", "type": "timeseries",
"gridPos": { "h": 6, "w": 8, "x": 8, "y": 0 },
"datasource": { "type": "loki", "uid": "bfowk7qjoccu8b" },
"targets": [ { "expr": "count(count by (session_id) (count_over_time({app=\"farmafinder-mobile\"}[5m])))", "legendFormat": "sessions" } ]
},
{
"id": 3, "title": "Errors/sec", "type": "timeseries",
"gridPos": { "h": 6, "w": 8, "x": 16, "y": 0 },
"datasource": { "type": "loki", "uid": "bfowk7qjoccu8b" },
"targets": [ { "expr": "sum(count_over_time({app=\"farmafinder-mobile\"} |~ \"(?i)error|exception|crash\"[5m]))", "legendFormat": "errors/s" } ]
},
{
"id": 4, "title": "Events by type", "type": "timeseries",
"gridPos": { "h": 7, "w": 12, "x": 0, "y": 6 },
"datasource": { "type": "loki", "uid": "bfowk7qjoccu8b" },
"targets": [ { "expr": "sum by (type) (count_over_time({app=\"farmafinder-mobile\"}[5m]))", "legendFormat": "{{type}}" } ]
},
{
"id": 5, "title": "Top error messages", "type": "timeseries",
"gridPos": { "h": 7, "w": 12, "x": 12, "y": 6 },
"datasource": { "type": "loki", "uid": "bfowk7qjoccu8b" },
"targets": [ { "expr": "sum by (message) (count_over_time({app=\"farmafinder-mobile\"} |~ \"(?i)error|exception|crash\"[5m]))", "legendFormat": "{{message}}" } ]
},
{
"id": 6, "title": "Recent logs", "type": "logs",
"gridPos": { "h": 10, "w": 24, "x": 0, "y": 13 },
"datasource": { "type": "loki", "uid": "bfowk7qjoccu8b" },
"targets": [ { "expr": "{app=\"farmafinder-mobile\"}", "legendFormat": "" } ]
}
],
"schemaVersion": 39,
"style": "dark",
"tags": ["farmafinder", "mobile", "rum"],
"templating": { "list": [] },
"time": { "from": "now-6h", "to": "now" },
"timepicker": {},
"timezone": "browser",
"title": "FarmaFinder — Mobile RUM",
"uid": "farmafinder-mobile-rum",
"version": 1,
"weekStart": ""
}