3. Logovi i LogQL — kako tražiš

↑ Nazad

Prvo pogledaj

Kako je Loki organizovan (bitno!)

Loki ne indeksira tekst logova, nego samo labele. Zato upit uvek počinje izborom labela u vitičastim zagradama {...}, pa tek onda filtriraš tekst. Što preciznije labele — to brži upit.

Tvoje labele (svaki log ima neke od ovih):

LabelaZnačenjePrimeri vrednosti kod tebe
hostmašinaubuntu88 (za sada jedina)
jobodakle je log došaodocker, files, nginx, systemd-journal
service_nameime servisa — ovo koristi najčešćeradarr, plex, jellyfin, nextcloud, nginx, ssh.service...
containerime Docker kontejneraradarr, uptime-kuma, crypto_scanner-db-1...
compose_projectdocker compose projekatobservability, crypto_scanner...
unitsystemd jedinica (za systemd-journal)ssh.service, snap.mosquitto.mosquitto.service, cron.service
levelnivo iz journal-a / nextcloud-aerr, warning, info, error...
log_typevrsta fajlanginx: access/error; nextcloud: app/error/access/script
streamstdout ili stderr kontejnerastdout, stderr

Pored labela, Loki sam prepoznaje nivo svake linije i stavlja ga u polje detected_level (error, warn, info, debug...). To nije labela nego "structured metadata", pa se piše posle |.

LogQL šalabahter (svi upiti provereni na tvom Lokiju)

Kucaj u Explore → izvor Loki → prebaci na Code režim.

1. Izbor logova (selektor)

{service_name="radarr"}                      svi logovi radarr-a
{container=~"crypto_scanner.*"}              svi kontejneri čije ime počinje sa crypto_scanner (=~ je regex)
{job="systemd-journal", unit="ssh.service"}  SSH prijave
{job="nginx", log_type="error"}              nginx greške
{service_name="nextcloud", log_type="app"}   Nextcloud aplikacioni log
{host="ubuntu88", service_name!="nginx"}     sve osim nginx-a (!= je "nije")

2. Filtriranje teksta (dodaje se posle selektora)

{service_name="plex"} |= "error"                     linije koje SADRŽE "error" (razlikuje velika/mala slova)
{service_name="plex"} |~ "(?i)error|fail"            regex, (?i) = ne gleda velika/mala slova
{service_name="plex"} != "healthcheck"               izbaci linije sa "healthcheck"
{job="systemd-journal"} |= "Failed with result"      pali systemd servisi
{container="uptime-kuma"} |= "Failing"               Kuma monitori koji padaju

3. Filtriranje po nivou

{host="ubuntu88"} | detected_level="error"
{host="ubuntu88", job!="nginx"} | detected_level=~"error|fatal|critical"

Ovaj drugi je tačno upit na kom radi tvoj alarm "Greske u logovima".

4. Brojanje (od logova pravi brojeve → grafikon)

count_over_time({service_name="radarr"} [5m])
    koliko linija je radarr upisao u svakih 5 minuta

sum by (service_name) (count_over_time({host="ubuntu88"} | detected_level="error" [1h]))
    broj grešaka po servisu, po satu

topk(10, sum by (service_name) (count_over_time({host="ubuntu88"} | detected_level="error" [24h])))
    10 servisa sa najviše grešaka u poslednja 24h

sum by (job) (count_over_time({host="ubuntu88"} [5m]))
    koliko logova stiže iz svakog izvora (ako docker padne na 0 — sakupljač ne radi)

U Explore za ovakve upite stavi Query type: Range i dobićeš grafikon kroz vreme.

5. Parsiranje — izvlačenje polja iz linije

nginx access log nema strukturu, ali ima uvek isti oblik pa ga pattern rastavlja:

sum by (status) (count_over_time({job="nginx", log_type="access"}
  | pattern `<_> - <_> [<_>] "<_> <_> <_>" <status> <_>` [1h]))

To daje broj odgovora po HTTP statusu (200, 404, 502...) po satu. Za JSON logove (npr. Nextcloud) postoji | json, za key=value logove | logfmt — posle njih polja možeš da filtriraš: | json | user="kimi".

Realni primeri iz tvog sistema

{service_name="whisparr"} | detected_level="error"
    whisparr i collabora su trenutno najglasniji (~1000 grešaka dnevno svaki) — dobar prvi zadatak da vidiš zašto

{container="uptime-kuma"} |= "Failing"
    koji Kuma monitori padaju i koliko često

{job="systemd-journal", unit="ssh.service"} |= "Accepted"
    ko se i kad prijavio preko SSH-a

{job="nginx", log_type="access"} |= " 502 "
    zahtevi na koje je nginx odgovorio 502 (servis iza njega ne radi)

Česte greške

SimptomUzrokRešenje
"No data"Vremenski opseg prekratak, ili logovi stariji od 14 danaProširi opseg gore desno
parse errorNavodnici: vrednosti labela idu u dvostruke navodnike; regex sa obrnutom kosom crtom ide u backtick navodnikePrepiši navodnike
Upit je spor ili "too many outstanding requests"Selektor {host="ubuntu88"} vuče sveSuzi na service_name / job i kraći opseg
detected_level ne radi u selektoruNije labelaPiši ga posle vitičastih zagrada, iza uspravne crte (primer 3 gore)
Upit sa count_over_time vraća listu umesto grafikaQuery type je InstantPrebaci na Range

Dokumentacija

Sledeće: 4. Dashboard i statistike

Izvor: 03-logovi-i-logql.md