Statistike kod tebe trenutno dolaze iz logova (broj grešaka, broj zahteva, HTTP statusi). Metrike tipa CPU/RAM/disk još nisu povezane — to je sledeći korak projekta (stranica 10).
Napravio sam ti dashboard sa 8 panela; svi upiti su testirani na tvom Lokiju. Fajl je u repou: E:\Work\AI\observability\grafana\dashboards\homelab-pregled.json (ceo JSON je i na dnu ove stranice).
Šta dobijaš:
| Panel | Šta pokazuje | Na šta da paziš |
|---|---|---|
| Greske u izabranom periodu | Ukupan broj error/fatal linija (bez nginx-a) | Normalno je ~3000 dnevno; nagli skok = nešto se desilo |
| nginx 5xx odgovori | Koliko puta je nginx vratio grešku servera | Desetine = servis iza nginx-a pada |
| Alarmi (stanje sada) | Svih 5 alarmnih pravila i njihovo stanje | Sve treba da je Normal |
| Greske po servisu kroz vreme | Stubići grešaka po servisu | Koji servis i kada je počeo |
| Top 10 po greskama | Rang lista najglasnijih servisa | Tvoja lista "šta treba srediti" |
| nginx zahtevi po statusu | 2xx/3xx/4xx/5xx kroz vreme | Crvena linija (5xx) treba da je ravna |
| Kolicina logova po izvoru | docker/files/nginx/journal | Ako bilo koja linija padne na 0 — sakupljač ili izvor ne radi |
| Poslednje greske | Lista samih linija | Klik na red = detalji |
Gore levo je padajuća lista Servis — izaberi jedan ili više servisa i svi paneli se filtriraju.
Cilj: panel "SSH prijave po satu".
Loki. Prebaci na Code i upiši: sum(count_over_time({job="systemd-journal", unit="ssh.service"} |= "Accepted" [$__auto]))SSH prijave. Gore Back to dashboard.$__auto znači "Grafana sama bira veličinu intervala prema vremenskom opsegu" — koristi ga uvek umesto fiksnog [5m] u panelima. $__range znači "ceo izabrani period" (za panele koji pokazuju jedan broj).
| Panel | Za šta | Primer upita |
|---|---|---|
| Time series | Linija/stubići kroz vreme | greške po minutu |
| Stat | Jedan veliki broj sa bojom | ukupno grešaka danas (Query type: Instant + $__range) |
| Bar gauge | Rang lista | top 10 servisa |
| Logs | Same linije logova | poslednje greške |
| Table | Tabela | broj grešaka po servisu i nivou |
| Alert list | Stanje alarma | nema upita |
Dashboard settings (zupčanik) → Variables → New variable:
Loki, Query type: Label values, Label: service_name..+.service_name=~"$servis" (sa =~, jer vrednost može biti više servisa).Tačno tako je napravljena lista "Servis" u uvezenom dashboard-u — otvori je i pogledaj.
grafana/dashboards/ — tako ga nikad ne gubiš.$__auto, label values): https://grafana.com/docs/grafana/latest/datasources/loki/template-variables/{
"title": "Homelab pregled",
"uid": "homelab-pregled",
"tags": ["homelab", "loki"],
"timezone": "browser",
"time": { "from": "now-24h", "to": "now" },
"refresh": "1m",
"schemaVersion": 39,
"editable": true,
"templating": {
"list": [
{
"name": "servis",
"label": "Servis",
"type": "query",
"datasource": { "type": "loki", "uid": "loki" },
"query": { "refId": "servis", "type": 1, "label": "service_name", "stream": "{host=\"ubuntu88\"}" },
"definition": "label_values({host=\"ubuntu88\"}, service_name)",
"includeAll": true,
"multi": true,
"allValue": ".+",
"current": { "text": ["All"], "value": ["$__all"] },
"refresh": 2,
"sort": 1
}
]
},
"panels": [
{
"id": 1, "type": "stat", "title": "Greske u izabranom periodu",
"description": "Linije sa nivoom error/fatal/critical (bez nginx-a). Broj zavisi od vremenskog opsega gore desno.",
"gridPos": { "x": 0, "y": 0, "w": 6, "h": 5 },
"datasource": { "type": "loki", "uid": "loki" },
"targets": [{ "refId": "A", "queryType": "instant", "instant": true,
"expr": "sum(count_over_time({host=\"ubuntu88\", job!=\"nginx\", service_name=~\"$servis\"} | detected_level=~\"error|fatal|critical\" [$__range]))" }],
"options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "background", "graphMode": "none" },
"fieldConfig": { "defaults": { "unit": "short", "thresholds": { "mode": "absolute",
"steps": [{ "color": "green", "value": null }, { "color": "orange", "value": 1000 }, { "color": "red", "value": 5000 }] } }, "overrides": [] }
},
{
"id": 2, "type": "stat", "title": "nginx 5xx odgovori",
"description": "Odgovori 500-599 (servis iza nginx-a pao ili puca).",
"gridPos": { "x": 6, "y": 0, "w": 6, "h": 5 },
"datasource": { "type": "loki", "uid": "loki" },
"targets": [{ "refId": "A", "queryType": "instant", "instant": true,
"expr": "sum(count_over_time({job=\"nginx\", log_type=\"access\"} | pattern `<_> - <_> [<_>] \"<_> <_> <_>\" <status> <_>` | status=~\"5..\" [$__range]))" }],
"options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "background", "graphMode": "none" },
"fieldConfig": { "defaults": { "unit": "short", "noValue": "0", "thresholds": { "mode": "absolute",
"steps": [{ "color": "green", "value": null }, { "color": "orange", "value": 10 }, { "color": "red", "value": 100 }] } }, "overrides": [] }
},
{
"id": 3, "type": "alertlist", "title": "Alarmi (stanje sada)",
"gridPos": { "x": 12, "y": 0, "w": 12, "h": 5 },
"options": { "viewMode": "list", "groupMode": "default", "maxItems": 10, "sortOrder": 1,
"stateFilter": { "firing": true, "pending": true, "noData": true, "error": true, "normal": true } }
},
{
"id": 4, "type": "timeseries", "title": "Greske po servisu kroz vreme",
"gridPos": { "x": 0, "y": 5, "w": 16, "h": 9 },
"datasource": { "type": "loki", "uid": "loki" },
"targets": [{ "refId": "A", "queryType": "range", "legendFormat": "{{service_name}}",
"expr": "sum by (service_name) (count_over_time({host=\"ubuntu88\", job!=\"nginx\", service_name=~\"$servis\"} | detected_level=~\"error|fatal|critical\" [$__auto]))" }],
"fieldConfig": { "defaults": { "custom": { "drawStyle": "bars", "fillOpacity": 80, "stacking": { "mode": "normal" } } }, "overrides": [] },
"options": { "legend": { "displayMode": "table", "placement": "right", "calcs": ["sum"], "sortBy": "Total", "sortDesc": true } }
},
{
"id": 5, "type": "bargauge", "title": "Top 10 po greskama",
"gridPos": { "x": 16, "y": 5, "w": 8, "h": 9 },
"datasource": { "type": "loki", "uid": "loki" },
"targets": [{ "refId": "A", "queryType": "instant", "instant": true, "legendFormat": "{{service_name}}",
"expr": "topk(10, sum by (service_name) (count_over_time({host=\"ubuntu88\", job!=\"nginx\", service_name=~\"$servis\"} | detected_level=~\"error|fatal|critical\" [$__range])))" }],
"options": { "orientation": "horizontal", "displayMode": "gradient", "reduceOptions": { "calcs": ["lastNotNull"] } },
"fieldConfig": { "defaults": { "color": { "mode": "continuous-GrYlRd" } }, "overrides": [] }
},
{
"id": 6, "type": "timeseries", "title": "nginx zahtevi po statusu (2xx/3xx/4xx/5xx)",
"gridPos": { "x": 0, "y": 14, "w": 12, "h": 8 },
"datasource": { "type": "loki", "uid": "loki" },
"targets": [{ "refId": "A", "queryType": "range", "legendFormat": "{{status}}",
"expr": "sum by (status) (count_over_time({job=\"nginx\", log_type=\"access\"} | pattern `<_> - <_> [<_>] \"<_> <_> <_>\" <status> <_>` | status=~\"[1-5]..\" | label_format status=`{{ substr 0 1 .status }}xx` [$__auto]))" }],
"fieldConfig": { "defaults": { "custom": { "fillOpacity": 15 } }, "overrides": [
{ "matcher": { "id": "byName", "options": "5xx" }, "properties": [{ "id": "color", "value": { "mode": "fixed", "fixedColor": "red" } }] },
{ "matcher": { "id": "byName", "options": "4xx" }, "properties": [{ "id": "color", "value": { "mode": "fixed", "fixedColor": "orange" } }] },
{ "matcher": { "id": "byName", "options": "2xx" }, "properties": [{ "id": "color", "value": { "mode": "fixed", "fixedColor": "green" } }] } ] }
},
{
"id": 7, "type": "timeseries", "title": "Kolicina logova po izvoru",
"description": "Ako linija 'docker' padne na nulu, sakupljac (Alloy) ne radi.",
"gridPos": { "x": 12, "y": 14, "w": 12, "h": 8 },
"datasource": { "type": "loki", "uid": "loki" },
"targets": [{ "refId": "A", "queryType": "range", "legendFormat": "{{job}}",
"expr": "sum by (job) (count_over_time({host=\"ubuntu88\"} [$__auto]))" }],
"fieldConfig": { "defaults": { "custom": { "fillOpacity": 10 } }, "overrides": [] }
},
{
"id": 8, "type": "logs", "title": "Poslednje greske (klik na red = detalji)",
"gridPos": { "x": 0, "y": 22, "w": 24, "h": 12 },
"datasource": { "type": "loki", "uid": "loki" },
"targets": [{ "refId": "A", "queryType": "range",
"expr": "{host=\"ubuntu88\", job!=\"nginx\", service_name=~\"$servis\"} | detected_level=~\"error|fatal|critical\"" }],
"options": { "showTime": true, "wrapLogMessage": true, "sortOrder": "Descending", "enableLogDetails": true, "dedupStrategy": "none" }
}
]
}Sledeće: 5. Alarmi i notifikacije