diff --git a/docker-compose.yml b/docker-compose.yml index 4c391ad50..7e7c78339 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -663,6 +663,7 @@ services: BOBBIN_LOG: info BOBBIN_KNOT_REQUIRE_HTTPS: "true" BOBBIN_INGEST_IDLE_PROMOTE_MIN_EVENTS: "1" + BOBBIN_OTLP_ENDPOINT: ${BOBBIN_OTLP_ENDPOINT:-} volumes: - .:/src:cached - bobbin-cargo:/cargo @@ -924,6 +925,7 @@ services: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--enable-feature=exemplar-storage' + - '--enable-feature=otlp-write-receiver' volumes: - prometheus-data:/prometheus - ./localinfra/observability/prometheus:/etc/prometheus:ro @@ -960,6 +962,20 @@ services: retries: 3 networks: [tngl] + # routes otlp http to prometheus and loki without running a full collector + otlp-gateway: + image: docker.io/library/nginx:1.27-alpine + profiles: ["observability"] + restart: unless-stopped + volumes: + - ./localinfra/observability/otlp-gateway.conf:/etc/nginx/conf.d/default.conf:ro + depends_on: + loki: + condition: service_started + prometheus: + condition: service_started + networks: [tngl] + grafana: diff --git a/localinfra/observability/grafana/provisioning/dashboards/bobbin.json b/localinfra/observability/grafana/provisioning/dashboards/bobbin.json new file mode 100644 index 000000000..d0120730d --- /dev/null +++ b/localinfra/observability/grafana/provisioning/dashboards/bobbin.json @@ -0,0 +1,1777 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": null, + "links": [], + "schemaVersion": 39, + "tags": [ + "bobbin" + ], + "templating": { + "list": [ + { + "current": {}, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".+", + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "definition": "label_values(bobbin_ingest_ready, job)", + "hide": 0, + "includeAll": true, + "label": "Service", + "multi": true, + "name": "job", + "options": [], + "query": { + "query": "label_values(bobbin_ingest_ready, job)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "sort": 1, + "type": "query" + }, + { + "allValue": ".+", + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "definition": "label_values(bobbin_ingest_ready{job=~\"$job\"}, instance)", + "hide": 0, + "includeAll": true, + "label": "Instance", + "multi": true, + "name": "instance", + "options": [], + "query": { + "query": "label_values(bobbin_ingest_ready{job=~\"$job\"}, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m" + ] + }, + "timezone": "browser", + "title": "Bobbin", + "uid": "bobbin-overview", + "version": 1, + "weekStart": "", + "refresh": "30s", + "panels": [ + { + "type": "row", + "title": "overview", + "collapsed": false, + "id": 1, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "panels": [] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 2, + "title": "ingest", + "description": "whether ingest has caught up with the firehose", + "type": "stat", + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 1 + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "mappings": [ + { + "type": "value", + "options": { + "0": { + "text": "warming", + "color": "orange" + }, + "1": { + "text": "ready", + "color": "green" + } + } + } + ], + "noValue": "No data" + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "min(bobbin_ingest_ready{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "ingest", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 3, + "title": "hydrant stream", + "description": "whether the hydrant firehose is connected", + "type": "stat", + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 1 + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "mappings": [ + { + "type": "value", + "options": { + "0": { + "text": "disconnected", + "color": "red" + }, + "1": { + "text": "connected", + "color": "green" + } + } + } + ], + "noValue": "No data" + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "min(bobbin_ingest_connected{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "hydrant stream", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 4, + "title": "events", + "description": "hydrant events committed per second", + "type": "stat", + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 1 + }, + "fieldConfig": { + "defaults": { + "unit": "ops", + "mappings": [], + "noValue": "No data" + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(rate(bobbin_ingest_events_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "events", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 5, + "title": "xrpc requests", + "description": "xrpc requests per second", + "type": "stat", + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 1 + }, + "fieldConfig": { + "defaults": { + "unit": "reqps", + "mappings": [], + "noValue": "No data" + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(rate(bobbin_xrpc_requests_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "xrpc requests", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 6, + "title": "xrpc 5xx", + "description": "share of xrpc requests answered with a 5xx", + "type": "stat", + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 1 + }, + "fieldConfig": { + "defaults": { + "unit": "percentunit", + "mappings": [], + "noValue": "No data" + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "(sum(rate(bobbin_xrpc_requests_total{job=~\"$job\", instance=~\"$instance\", status=~\"5..\"}[$__rate_interval])) or vector(0)) / sum(rate(bobbin_xrpc_requests_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "xrpc 5xx", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 7, + "title": "resident memory", + "description": "resident set size across instances", + "type": "stat", + "gridPos": { + "h": 4, + "w": 4, + "x": 20, + "y": 1 + }, + "fieldConfig": { + "defaults": { + "unit": "bytes", + "mappings": [], + "noValue": "No data" + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(process_resident_memory_bytes{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "resident memory", + "range": true, + "refId": "A" + } + ] + }, + { + "type": "row", + "title": "ingest", + "collapsed": false, + "id": 8, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "panels": [] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 9, + "title": "records by outcome", + "description": "records off the firehose per second, by what ingest did with them", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "fieldConfig": { + "defaults": { + "unit": "ops", + "custom": { + "lineWidth": 1, + "fillOpacity": 40, + "stacking": { + "mode": "normal" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (outcome) (rate(bobbin_ingest_records_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "{{outcome}}", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 10, + "title": "rejections", + "description": "rejected records per second by reason and collection. unknown collections show up as other", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "fieldConfig": { + "defaults": { + "unit": "ops", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (reason, collection) (rate(bobbin_ingest_records_total{job=~\"$job\", instance=~\"$instance\", outcome=\"rejected\"}[$__rate_interval]))", + "legendFormat": "{{reason}} {{collection}}", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 11, + "title": "dropped before indexing", + "description": "record frames and edges dropped before they reached the index", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "fieldConfig": { + "defaults": { + "unit": "ops", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (reason) (rate(bobbin_ingest_frames_dropped_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "frame {{reason}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (reason) (rate(bobbin_ingest_edges_dropped_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "edge {{reason}}", + "range": true, + "refId": "B" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 12, + "title": "hydrant disconnects", + "description": "hydrant sessions that ended in an error, by kind", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (kind) (increase(bobbin_ingest_disconnects_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "{{kind}}", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 13, + "title": "parked upserts", + "description": "upserts waiting on repo did resolution, and what happens to them", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 22 + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(bobbin_ingest_parked{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "parked now", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (outcome) (rate(bobbin_ingest_parked_outcomes_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "{{outcome}} /s", + "range": true, + "refId": "B" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 14, + "title": "posthog events", + "description": "posthog events per second by whether they reached posthog", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 22 + }, + "fieldConfig": { + "defaults": { + "unit": "ops", + "custom": { + "lineWidth": 1, + "fillOpacity": 40, + "stacking": { + "mode": "normal" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (result) (rate(bobbin_ingest_analytics_events_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "{{result}}", + "range": true, + "refId": "A" + } + ] + }, + { + "type": "row", + "title": "xrpc", + "collapsed": false, + "id": 15, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 30 + }, + "panels": [] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 16, + "title": "requests by route", + "description": "xrpc requests per second by route", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "fieldConfig": { + "defaults": { + "unit": "reqps", + "custom": { + "lineWidth": 1, + "fillOpacity": 40, + "stacking": { + "mode": "normal" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (route) (rate(bobbin_xrpc_requests_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "{{route}}", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 17, + "title": "errors by status", + "description": "4xx and 5xx responses per second", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "fieldConfig": { + "defaults": { + "unit": "reqps", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (status, route) (rate(bobbin_xrpc_requests_total{job=~\"$job\", instance=~\"$instance\", status=~\"[45]..\"}[$__rate_interval]))", + "legendFormat": "{{status}} {{route}}", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 18, + "title": "latency", + "description": "xrpc latency quantiles across routes", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 39 + }, + "fieldConfig": { + "defaults": { + "unit": "s", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum by (le) (rate(bobbin_xrpc_duration_seconds_bucket{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval])))", + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum by (le) (rate(bobbin_xrpc_duration_seconds_bucket{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval])))", + "legendFormat": "p95", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum by (le) (rate(bobbin_xrpc_duration_seconds_bucket{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval])))", + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 19, + "title": "p95 latency by route", + "description": "95th percentile xrpc latency per route", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 39 + }, + "fieldConfig": { + "defaults": { + "unit": "s", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum by (le, route) (rate(bobbin_xrpc_duration_seconds_bucket{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval])))", + "legendFormat": "{{route}}", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 20, + "title": "in flight", + "description": "xrpc requests being handled right now", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 47 + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "custom": { + "lineWidth": 1, + "fillOpacity": 40, + "stacking": { + "mode": "normal" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (route) (bobbin_xrpc_in_flight{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "{{route}}", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 21, + "title": "heavy requests", + "description": "heavy requests in flight against the limit memory pressure sets, and requests shed as overloaded", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 47 + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (kind) (bobbin_heavy_requests{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "{{kind}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(rate(bobbin_heavy_rejected_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "rejected /s", + "range": true, + "refId": "B" + } + ] + }, + { + "type": "row", + "title": "resolvers and caches", + "collapsed": false, + "id": 22, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 55 + }, + "panels": [] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 23, + "title": "repo did lookups", + "description": "repo did lookups per second by result", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 56 + }, + "fieldConfig": { + "defaults": { + "unit": "ops", + "custom": { + "lineWidth": 1, + "fillOpacity": 40, + "stacking": { + "mode": "normal" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (result) (rate(bobbin_resolver_lookups_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "{{result}}", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 24, + "title": "repo did miss latency", + "description": "average time a repo did miss spent upstream", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 56 + }, + "fieldConfig": { + "defaults": { + "unit": "s", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(rate(bobbin_resolver_miss_latency_seconds_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval])) / sum(rate(bobbin_resolver_lookups_total{job=~\"$job\", instance=~\"$instance\", result!~\"hit|no_client\"}[$__rate_interval]))", + "legendFormat": "avg", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 25, + "title": "identity cache", + "description": "identity cache hit ratio, and background warming by result", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 64 + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(rate(bobbin_identity_lookups_total{job=~\"$job\", instance=~\"$instance\", result=\"hit\"}[$__rate_interval])) / sum(rate(bobbin_identity_lookups_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "hit ratio", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (result) (rate(bobbin_identity_warm_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "warm {{result}} /s", + "range": true, + "refId": "B" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 26, + "title": "record cache", + "description": "bytes the record cache holds against its capacity", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 64 + }, + "fieldConfig": { + "defaults": { + "unit": "bytes", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (kind) (bobbin_record_cache_size_bytes{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "{{kind}}", + "range": true, + "refId": "A" + } + ] + }, + { + "type": "row", + "title": "process", + "collapsed": false, + "id": 27, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 72 + }, + "panels": [] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 28, + "title": "memory", + "description": "resident set, cgroup usage against the budget, and jemalloc's view", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 73 + }, + "fieldConfig": { + "defaults": { + "unit": "bytes", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(process_resident_memory_bytes{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "rss", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (kind) (bobbin_memory_bytes{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "cgroup {{kind}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (kind) (bobbin_jemalloc_bytes{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "jemalloc {{kind}}", + "range": true, + "refId": "C" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 29, + "title": "cpu", + "description": "cpu cores in use", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 73 + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (instance) (rate(process_cpu_seconds_total{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval]))", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 30, + "title": "threads and file descriptors", + "description": "os threads and open file descriptors", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 81 + }, + "fieldConfig": { + "defaults": { + "unit": "short", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (instance) (process_threads{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "threads {{instance}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by (instance) (process_open_fds{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "fds {{instance}}", + "range": true, + "refId": "B" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "id": 31, + "title": "uptime", + "description": "time since each instance started", + "type": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 81 + }, + "fieldConfig": { + "defaults": { + "unit": "s", + "custom": { + "lineWidth": 1, + "fillOpacity": 10, + "stacking": { + "mode": "none" + } + } + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "table", + "placement": "bottom", + "calcs": [ + "lastNotNull", + "max" + ] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "time() - max by (instance) (process_start_time_seconds{job=~\"$job\", instance=~\"$instance\"})", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ] + }, + { + "type": "row", + "title": "logs", + "collapsed": false, + "id": 32, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 89 + }, + "panels": [] + }, + { + "datasource": { + "type": "loki", + "uid": "loki" + }, + "id": 33, + "title": "warnings and errors", + "type": "logs", + "description": "bobbin log lines at warn or above", + "gridPos": { + "h": 12, + "w": 24, + "x": 0, + "y": 90 + }, + "options": { + "showTime": true, + "wrapLogMessage": true, + "sortOrder": "Descending", + "enableLogDetails": true + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "loki" + }, + "editorMode": "code", + "expr": "{service_name=~\"$job\"} | severity_text=~\"WARN|ERROR\"", + "queryType": "range", + "refId": "A" + } + ] + } + ] +} diff --git a/localinfra/observability/otlp-gateway.conf b/localinfra/observability/otlp-gateway.conf new file mode 100644 index 000000000..67efd52ce --- /dev/null +++ b/localinfra/observability/otlp-gateway.conf @@ -0,0 +1,12 @@ +# routes standard otlp http paths to prometheus and loki without running a full collector +server { + listen 4318; + # otlp batches easily exceed nginx's 1m default + client_max_body_size 16m; + location = /v1/logs { + proxy_pass http://loki:3100/otlp/v1/logs; + } + location = /v1/metrics { + proxy_pass http://prometheus:9090/api/v1/otlp/v1/metrics; + } +} diff --git a/localinfra/readme.md b/localinfra/readme.md index 07055bb8b..b22cad7e5 100644 --- a/localinfra/readme.md +++ b/localinfra/readme.md @@ -101,6 +101,8 @@ docker compose --profile linux --profile observability up The endpoint values above are addresses on the Compose network. Leaving either endpoint empty disables that OTLP exporter; Prometheus metrics and JSON logs on stderr remain enabled. +Set `BOBBIN_OTLP_ENDPOINT=http://otlp-gateway:4318` to send bobbin's logs and metrics through the gateway, which routes them to Loki and Prometheus. + The profile exposes: - Grafana at , with the `Spindle Overview` dashboard and Prometheus, Tempo, and Loki datasources provisioned