4. Dashboard i statistike

↑ Nazad

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).

Prvo pogledaj

Korak 1 — uvezi gotov dashboard "Homelab pregled"

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).

  1. Grafana → Dashboards → dugme New → Import.
  2. Upload dashboard JSON file → izaberi gornji fajl (ili nalepi JSON sa dna stranice u polje).
  3. Load → Import.

Šta dobijaš:

PanelŠta pokazujeNa šta da paziš
Greske u izabranom perioduUkupan broj error/fatal linija (bez nginx-a)Normalno je ~3000 dnevno; nagli skok = nešto se desilo
nginx 5xx odgovoriKoliko puta je nginx vratio grešku serveraDesetine = servis iza nginx-a pada
Alarmi (stanje sada)Svih 5 alarmnih pravila i njihovo stanjeSve treba da je Normal
Greske po servisu kroz vremeStubići grešaka po servisuKoji servis i kada je počeo
Top 10 po greskamaRang lista najglasnijih servisaTvoja lista "šta treba srediti"
nginx zahtevi po statusu2xx/3xx/4xx/5xx kroz vremeCrvena linija (5xx) treba da je ravna
Kolicina logova po izvorudocker/files/nginx/journalAko bilo koja linija padne na 0 — sakupljač ili izvor ne radi
Poslednje greskeLista samih linijaKlik na red = detalji

Gore levo je padajuća lista Servis — izaberi jedan ili više servisa i svi paneli se filtriraju.

Korak 2 — napravi svoj prvi panel (vežba)

Cilj: panel "SSH prijave po satu".

  1. Na dashboard-u gore Edit → Add → Visualization.
  2. Izvor: Loki. Prebaci na Code i upiši: sum(count_over_time({job="systemd-journal", unit="ssh.service"} |= "Accepted" [$__auto]))
  3. Desno, Visualization → Time series (ili Bar chart).
  4. Title: SSH prijave. Gore Back to dashboard.
  5. Save dashboard (ikonica diskete). Grafana ne čuva sama — ako zaboraviš, panel nestaje.

$__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).

Vrste panela koje ćeš koristiti

PanelZa štaPrimer upita
Time seriesLinija/stubići kroz vremegreške po minutu
StatJedan veliki broj sa bojomukupno grešaka danas (Query type: Instant + $__range)
Bar gaugeRang listatop 10 servisa
LogsSame linije logovaposlednje greške
TableTabelabroj grešaka po servisu i nivou
Alert listStanje alarmanema upita

Promenljive (padajuće liste)

Dashboard settings (zupčanik) → Variables → New variable:

Tačno tako je napravljena lista "Servis" u uvezenom dashboard-u — otvori je i pogledaj.

Saveti

Još materijala

JSON "Homelab pregled" (za copy-paste u Import)

{
  "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

Izvor: 04-dashboard-i-statistike.md