#!/usr/bin/env python3 # Генератор дашборда Grafana "GoToSocial" — по стилю vesti.json # Результат: grafana/dashboards/gotosocial/gotosocial.json import json, os, uuid DS = {"type": "prometheus", "uid": "Prometheus"} F = "bigbox:9464" def tgt(expr, legend="", ref="A"): return {"datasource": DS, "expr": expr, "legendFormat": legend, "refId": ref} def stat_panel(title, expr, x, y, w=4, h=3, mapping=None, unit="short", thresholds=None): if mapping is None: mapping = {"0": {"color": "red", "text": "DOWN"}, "1": {"color": "green", "text": "UP"}} if thresholds is None: thresholds = {"mode": "absolute", "steps": [ {"color": "red", "value": None}, {"color": "green", "value": 1}]} return { "datasource": DS, "fieldConfig": {"defaults": { "color": {"mode": "thresholds"}, "mappings": [{"options": mapping, "type": "value"}], "thresholds": thresholds, "unit": unit}, "overrides": []}, "gridPos": {"h": h, "w": w, "x": x, "y": y}, "id": uuid.uuid4().int & 0xFFFF, "options": { "colorMode": "background", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": False}, "textMode": "auto"}, "pluginVersion": "11.1.0", "targets": [tgt(expr, "")], "title": title, "type": "stat", } def ts_panel(title, exprs, x, y, w=12, h=4, unit="short", legend=False): # exprs: list of (expr, legend) tuples return { "datasource": DS, "fieldConfig": {"defaults": { "color": {"mode": "palette-classic"}, "custom": { "axisCenteredZero": False, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": {"legend": False, "tooltip": False, "viz": False}, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": {"type": "linear"}, "showPoints": "never", "spanNulls": False, "stacking": {"group": "A", "mode": "none"}, "thresholdsStyle": {"mode": "off"}}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [ {"color": "green", "value": None}]}, "unit": unit}, "overrides": []}, "gridPos": {"h": h, "w": w, "x": x, "y": y}, "id": uuid.uuid4().int & 0xFFFF, "options": {"legend": {"calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": legend}, "tooltip": {"mode": "multi", "sort": "none"}}, "targets": [tgt(e, l) for e, l in exprs], "title": title, "type": "timeseries", } def row(title, y): return {"collapsed": False, "gridPos": {"h": 1, "w": 24, "x": 0, "y": y}, "id": uuid.uuid4().int & 0xFFFF, "panels": [], "title": title, "type": "row"} panels = [] y = 0 # ---- Доступность ---- panels.append(row("Доступность", y)); y += 1 panels.append(stat_panel("Инстанс жив (total users)", f'gotosocial_instance_total_users{{instance="{F}"}}', 0, y, w=8, h=3)); y += 3 # ---- Instance ---- panels.append(row("Инстанс", y)); y += 1 panels.append(ts_panel("Пользователи", [(f'gotosocial_instance_total_users{{instance="{F}"}}', "users")], 0, y, w=8, h=4)) panels.append(ts_panel("Статусы", [(f'gotosocial_instance_total_statuses{{instance="{F}"}}', "statuses")], 8, y, w=8, h=4)) panels.append(ts_panel("Федерирующиеся инстансы", [(f'gotosocial_instance_total_federating_instances{{instance="{F}"}}', "federating")], 16, y, w=8, h=4)); y += 4 # ---- Workers ---- panels.append(row("Воркеры", y)); y += 1 workers = ["client_api", "fedi_api", "processing", "delivery", "dereference", "webpush"] count_exprs = [(f'gotosocial_workers_{w}_count{{instance="{F}"}}', w) for w in workers] queue_exprs = [(f'gotosocial_workers_{w}_queue{{instance="{F}"}}', w) for w in workers] panels.append(ts_panel("Воркеры: count (обработано)", count_exprs, 0, y, w=12, h=4, legend=True)) panels.append(ts_panel("Воркеры: queue (очередь)", queue_exprs, 12, y, w=12, h=4, legend=True)); y += 4 # ---- HTTP ---- panels.append(row("HTTP", y)); y += 1 panels.append(ts_panel("Запросы/с (все routes)", [(f'rate(http_server_requests_total{{otel_scope_name="gin",instance="{F}"}})', "req/s")], 0, y, w=8, h=4)) panels.append(ts_panel("Активные запросы", [(f'http_server_requests_active{{instance="{F}"}}', "active")], 8, y, w=8, h=4)) panels.append(ts_panel("Длительность (p50)", [(f'http_server_duration_milliseconds{{quantile="0.5",instance="{F}"}}', "p50 ms")], 16, y, w=8, h=4)); y += 4 # ---- SQL ---- panels.append(row("SQL", y)); y += 1 panels.append(ts_panel("SQL соединения", [(f'go_sql_connections_open{{instance="{F}"}}', "open"), (f'go_sql_connections_in_use{{instance="{F}"}}', "in_use")], 0, y, w=12, h=4, legend=True)) panels.append(ts_panel("SQL запросы (p50 ms)", [(f'go_sql_query_timing_milliseconds{{quantile="0.5",instance="{F}"}}', "p50")], 12, y, w=12, h=4)); y += 4 # ---- Процесс ---- panels.append(row("Процесс", y)); y += 1 panels.append(ts_panel("RSS память (GB)", [(f'process_resident_memory_bytes{{instance="{F}"}}/1024/1024/1024', "RSS GB")], 0, y, w=8, h=4)) panels.append(ts_panel("CPU (сек)", [(f'process_cpu_seconds_total{{instance="{F}"}}', "cpu s")], 8, y, w=8, h=4)) panels.append(ts_panel("Открытые FD", [(f'process_open_fds{{instance="{F}"}}', "fds")], 16, y, w=8, h=4)); y += 4 dash = { "annotations": {"list": []}, "editable": True, "fiscalYearStartMonth": 1, "graphTooltip": 0, "id": uuid.uuid4().int & 0xFFFF, "links": [], "panels": panels, "refresh": "30s", "tags": ["gotosocial"], "title": "GoToSocial", "type": "dashboard", "uid": "gotosocial-main", "version": 1, "schemaVersion": 1, } outdir = os.path.join(os.path.dirname(__file__), "..", "grafana", "dashboards", "gotosocial") os.makedirs(outdir, exist_ok=True) out = os.path.join(outdir, "gotosocial.json") with open(out, "w") as f: json.dump(dash, f, ensure_ascii=False, indent=1) print("OK:", out, len(panels), "panels")