{ "title": "Journal", "uid": "trails-journal", "annotations": { "list": [ { "name": "Deploys", "enable": true, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "iconColor": "rgba(0, 211, 255, 1)", "target": { "limit": 100, "matchAny": false, "tags": [ "deploy" ], "type": "tags" } } ] }, "timezone": "browser", "refresh": "30s", "panels": [ { "title": "Caddy 502 Rate (Journal)", "description": "502 errors returned by Caddy for the journal upstream. These indicate Caddy could not reach the journal container.", "type": "timeseries", "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }, "targets": [ { "expr": "sum(rate(caddy_http_request_duration_seconds_count{server=~\".*trails.cool.*\", code=\"502\"}[5m])) or vector(0)", "legendFormat": "502/s" } ], "fieldConfig": { "defaults": { "custom": { "fillOpacity": 20, "lineWidth": 2 }, "color": { "mode": "fixed", "fixedColor": "red" } } } }, { "title": "Caddy Response Codes (Journal)", "description": "All HTTP response codes from Caddy for the journal host, including Caddy-generated errors like 502.", "type": "timeseries", "gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }, "targets": [ { "expr": "sum(rate(caddy_http_request_duration_seconds_count{server=~\".*trails.cool.*\", code!~\"(101|2..|3..)\"}[5m])) by (code)", "legendFormat": "{{code}}" } ] }, { "title": "Journal Request Rate by Status", "description": "Request rate from the journal app's own metrics, grouped by HTTP status code.", "type": "timeseries", "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 }, "targets": [ { "expr": "sum(rate(http_request_duration_seconds_count{job=\"journal\"}[5m])) by (status)", "legendFormat": "{{status}}" } ] }, { "title": "Journal Latency p50 / p95 / p99", "type": "timeseries", "gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 }, "targets": [ { "expr": "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket{job=\"journal\"}[5m])) by (le))", "legendFormat": "p50" }, { "expr": "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job=\"journal\"}[5m])) by (le))", "legendFormat": "p95" }, { "expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{job=\"journal\"}[5m])) by (le))", "legendFormat": "p99" } ], "fieldConfig": { "defaults": { "unit": "s" } } }, { "title": "Request Rate by Route", "type": "timeseries", "gridPos": { "h": 8, "w": 24, "x": 0, "y": 16 }, "targets": [ { "expr": "sum(rate(http_request_duration_seconds_count{job=\"journal\"}[5m])) by (route)", "legendFormat": "{{route}}" } ] }, { "title": "Container Restarts", "description": "Journal container restart count. Spikes here correlate with 502 errors — Caddy returns 502 while the container is restarting.", "type": "timeseries", "gridPos": { "h": 8, "w": 8, "x": 0, "y": 24 }, "targets": [ { "expr": "changes(container_start_time_seconds{name=\"trails-cool-journal-1\"}[5m])", "legendFormat": "journal" } ], "fieldConfig": { "defaults": { "custom": { "fillOpacity": 30, "drawStyle": "bars" }, "color": { "mode": "fixed", "fixedColor": "orange" } } } }, { "title": "Container Memory", "type": "timeseries", "gridPos": { "h": 8, "w": 8, "x": 8, "y": 24 }, "targets": [ { "expr": "container_memory_usage_bytes{name=\"trails-cool-journal-1\"}", "legendFormat": "used" }, { "expr": "container_memory_working_set_bytes{name=\"trails-cool-journal-1\"}", "legendFormat": "working set" } ], "fieldConfig": { "defaults": { "unit": "bytes" } } }, { "title": "Container CPU", "type": "timeseries", "gridPos": { "h": 8, "w": 8, "x": 16, "y": 24 }, "targets": [ { "expr": "rate(container_cpu_usage_seconds_total{name=\"trails-cool-journal-1\"}[5m]) * 100", "legendFormat": "CPU %" } ], "fieldConfig": { "defaults": { "unit": "percent" } } }, { "title": "Node.js Event Loop Lag", "type": "timeseries", "gridPos": { "h": 8, "w": 12, "x": 0, "y": 32 }, "targets": [ { "expr": "nodejs_eventloop_lag_seconds{job=\"journal\"}", "legendFormat": "lag" } ], "fieldConfig": { "defaults": { "unit": "s" } } }, { "title": "Node.js Heap Used", "type": "timeseries", "gridPos": { "h": 8, "w": 12, "x": 12, "y": 32 }, "targets": [ { "expr": "nodejs_heap_size_used_bytes{job=\"journal\"}", "legendFormat": "used" }, { "expr": "nodejs_heap_size_total_bytes{job=\"journal\"}", "legendFormat": "total" } ], "fieldConfig": { "defaults": { "unit": "bytes" } } }, { "title": "Journal Logs (Errors & Warnings)", "description": "Recent error and warning logs from the journal container. Requires Promtail docker_sd_configs with Pino JSON parsing.", "type": "logs", "gridPos": { "h": 10, "w": 24, "x": 0, "y": 40 }, "targets": [ { "datasource": { "type": "loki", "uid": "loki" }, "expr": "{service=\"journal\"} |~ \"(error|warn|ERR|WARN|level.*(40|50|60))\"", "legendFormat": "" } ], "options": { "showTime": true, "sortOrder": "Descending", "enableLogDetails": true, "dedupStrategy": "none" } }, { "title": "Caddy Logs (5xx Responses)", "description": "Caddy access log entries with 5xx status codes. Shows the raw request that triggered the error.", "type": "logs", "gridPos": { "h": 10, "w": 24, "x": 0, "y": 50 }, "targets": [ { "datasource": { "type": "loki", "uid": "loki" }, "expr": "{service=\"caddy\"} |~ \"\\\"status\\\":\\s*5\\d\\d\"", "legendFormat": "" } ], "options": { "showTime": true, "sortOrder": "Descending", "enableLogDetails": true, "dedupStrategy": "none" } } ] }