Konferencja Mega Sekurak Hacking Party w Krakowie – 26-27 października!

Zabbix + grafana – część 6 – Parser UQL

16 marca 2026, 09:50 | Aktualności, Teksty | 0 komentarzy

Wstęp

W poprzednim artykule uporządkowaliśmy sprawy związane z konfiguracją pluginu Infinity i obsługą podstawowych zapytań API. Jeżeli po pierwszych testach z API Zabbix zauważyłeś, że wyniki przypominają bardziej losową zupę JSON niż dane gotowe do wizualizacji, to mam dobrą wiadomość: nie jesteś sam. Na szczęście, by rozwiązać ten problem, na scenę wchodzą parsery – narzędzia, które pozwalają ujarzmić surowe odpowiedzi API i zamienić je w dane, które da się zaprezentować bez wcześniejszego tłumaczenia „tak, wiem, wygląda to trochę dziwnie…”.

Dlatego w tej części przyjrzymy się pierwszemu z nich – parserowi UQL, który daje możliwość przekształcania wyników w sposób spójny, zrozumiały i oparty na czytelnej składni.

Jeżeli czytasz ten artykuł, to najprawdopodobniej znaczy, że konfigurację pluginu Infinity oraz pierwsze zapytania do API Zabbix masz już za sobą. A jeżeli trafiłeś tu przypadkiem i nie wiesz jeszcze, czym jest Infinity, jak tworzyć źródło danych ani jak wygląda podstawowa wysyłka zapytania do API Zabbix, to zdecydowanie zacznij od >poprzedniego artykułu.

Zaczynamy!

Ten artykuł to część serii przybliżającej integrację Grafany oraz Zabbixa przygotowanej przez Alberta Przybylskiego. Zachęcamy do zapoznania się ze wszystkimi tekstami dotyczącymi tego zagadnienia!

  1. Zabbix + Grafana – część 1 – instalacja
  2. Zabbix + Grafana – część 2 – Przedstawianie danych
  3. Zabbix + Grafana – część 3 – Przedstawianie danych – przykłady zaawansowane
  4. Zabbix + Grafana – część 4 – Pobieranie danych z DB
  5. Zabbix + Grafana – część 5 – Własne zapytania do API

Szybka powtórka

Na początek przypomnijmy sobie testowe środowisko:

Rysunek 1. Środowisko testowe wraz z oznaczoną komunikacją.

Dodatkowe założenia w tym przykładzie:

  • Nazwy hostów w Zabbix są zgodne z tymi skonfigurowanymi w systemie operacyjnym,
  • Obydwa hosty należą do grupy “Linux servers”,
  • Host “zabbix-7-0” jest dodatkowo w grupie “Zabbix servers”,
  • Istnieje już użytkownik “read-only” (opisany w >tym artykule) o nazwie grafana_api 

UQL

Zanim przejdziemy do realnego przykładu i pobierania danych z Zabbix API, musimy poznać narzędzia, które Infinity daje nam do obróbki danych po stronie Grafany. Pierwszym z nich jest wspomniane na początku UQL.

UQL (Unstructured Query Language) to prosty, przejrzysty i bardzo elastyczny język do pracy z danymi. UQL jest wykonywany bezpośrednio w przeglądarce klienta, co oznacza, że backend nie jest obciążany (może to być przydatne, gdy np. posiadasz niewielką instancję Grafany z minimalnymi zasobami, a potrzebujesz przetworzyć dużą ilość danych z API). Ceną za to jest jednak brak możliwości użycia niektórych mechanizmów Grafany na takim zestawie danych, takich jak alertowanie czy buforowanie odpowiedzi (niedostępne w Grafana OSS). Grafana zresztą będzie nam to regularnie przypominać podczas konfiguracji zapytania:

Rysunek 2. Przypomnienie o ograniczeniu funkcjonalności podczas używania UQL

Dlaczego więc zaczynamy od UQL?

Ponieważ jest najbardziej przystępny i posiada „czytelną” składnię inspirowaną Kusto Query Language (używanym m.in. przy wyciąganiu danych z chmury Azure). W praktyce wygląda to tak – piszemy krótką sekwencję poleceń oddzielonych symbolem |, każde w osobnej linii – tworząc coś w rodzaju mini-pipeline’u, który przetwarza dane krok po kroku.

Najprostszy szkielet UQL (w pracy z Zabbix) wygląda na przykład tak:

parse-json
| project "result"

Czyli:

parse-json – zamienia surową odpowiedź API (tekst) na obiekt JSON, na którym możemy wykonywać dalsze operacje,
project "result" – wybiera interesujące nas pole z odpowiedzi (w Zabbix API praktycznie wszystkie wyniki znajdują się właśnie w polu result).

To są absolutne podstawy pracy z UQL — prosty punkt wyjścia, od którego wygodnie zacząć, zanim przejdziemy do bardziej złożonych operacji.

Skoro fundamenty mamy już za sobą, możemy przejść do praktyki i sprawdzić, jak UQL sprawdza się w realnym scenariuszu. W naszym przykładzie chcemy pobrać listę interfejsów, które obecnie nie działają, czyli mają status niedostępny (ang. Not available) lub nieznany (ang. Unknown). Dodatkowo, dla interfejsów o statusie niedostępnym chcemy zebrać informację, od kiedy pozostają w tym stanie — dla statusu nieznany taka informacja nie jest dostępna. 

 Na początek przygotujemy zapytanie API, które pobiera listę niedostępnych interfejsów dla wybranej grupy hostów oraz czas, od kiedy dany interfejs jest niedostępny (jeżeli dotyczy). W tym celu użyjemy standardowej metody host.get w Zabbix API (po więcej szczegółów Autor odsyła do oficjalnej >dokumentacji metody, by czytelnik mógł zobaczyć, w jaki sposób jej używać). Bardziej zaznajomiony z API czytelnik mógłby się zapytać:

“Zaraz, czemu nie używasz metody ‘>hostinterface.get’, która służy właśnie do wyciągania interfejsów hostów?” 

Oczywiście metoda ta jest również poprawna, jednakże nie pozwala na filtrowanie per grupa hostów (a nie chcemy pobierać wszystkich interfejsów z całego środowiska lub filtrować tylko po hostach) oraz nie umożliwia wyfiltrowania wyniku tylko na hosty włączone (ang. Enabled).

Skoro mowa o grupie hostów, potrzebujemy jeszcze jednego założenia – znamy ID grupy, którą chcemy wykorzystać w zapytaniu. Można ją wyciągnąć na wiele sposobów – jednym z prostszych jest wejście w zakładkę Data collection – Host groups w interfejsie Zabbix, znaleźć interesującą nas grupę (w naszym przykładzie – Linux servers) i kliknięcie na nią – wyskoczy nam nowe okienko konfiguracji grupy. Naszą uwagę natomiast zamiast tego powinniśmy zwrócić na URL:
http://192.168.88.240/zabbix.php?action=hostgroup.edit&groupid=2
Wartość groupid (w tym przykładzie 2) to właśnie nasz identyfikator grupy, którego użyjemy w zapytaniu.

Skoro teorie mamy za sobą, przejdźmy do praktyki i utwórzmy nowy dashboard o nazwie “Przykładowy dashboard 3”:

Rysunek 3. Ustawienia nowego dashboardu “Przykładowy dashboard 3”

Jeżeli nie wiesz gdzie tworzy się nowy dashboard lub panel, odsyłam Cię do >poprzedniego artykułu, gdzie zostały wyjaśnione podstawowe czynności krok po kroku.

Tworzymy pierwszy panel. W sekcji Queries (na dole strony) ustawiamy źródło danych na wcześniej skonfigurowane zabbix-API-7-0 i konfigurujemy zapytanie w następujący sposób:

  • Type – pozostawiamy typ danych JSON
  • Parser – w tym przykładzie będziemy stosować UQL
  • Source – chcemy pobierać dane z API, a nie np. podawać je statycznie, więc zostawiamy URL
  • Format – pozostawiamy domyślną tabelę
  • Method – pamiętajmy, że Zabbix wykorzystuje wyłącznie metodę POST, dlatego każdorazowo musimy ją ustawić w tym miejscu
  • URL – gdy skonfigurowaliśmy źródło danych tak jak w sekcji Konfiguracja pluginu opisanego w >tym artykule, to możemy to pole zostawić puste; w przeciwnym wypadku nalezy wpisać pełny URL do API Zabbix

Kiedy podstawową konfigurację mamy za sobą, klikamy na przycisk Headers, Body, Request params:

Rysunek 4. Podstawowe ustawienia dla zapytania UQL

W sekcji Body Type pamiętajmy o zmianie Body Content type na JSON, natomiast w sekcji zawartości wklejamy następujące zapytanie:

{
  "jsonrpc": "2.0",
  "method": "host.get",
  "params": {
    "groupids": 2,
    "output": ["hostid","host"],
    "selectInterfaces": ["interfaceid","available","type","error","errors_from"],
    "filter": { "status": 0 }
  },
  "id": 1
}

Zatrzymajmy się na chwile i przeanalizujmy powyższe zapytanie krok po kroku:

"jsonrpc": "2.0"

Określa wersję protokołu JSON-RPC. W momencie pisania artykułu Zabbix wykorzystuje wersję 2.0.

"method": "host.get"

Nazwa metody, którą chcemy wykonać. W tym przykładzie jest to host.get.

"params": { …… }

Dodatkowe parametry metody. Każda metoda może posiadać inny zestaw parametrów, dlatego ich pełny opis zawsze warto sprawdzić w dokumentacji danej metody.

"id": 1

Identyfikator zapytania, który musi zostać zwrócony w odpowiedzi API. Może być pomocny przy debugowaniu, gdy wysyłamy wiele zapytań jednocześnie. W tym przykładzie nie ma to większego znaczenia, dlatego ustawiamy po prostu wartość 1.

Najważniejsza część tego zapytania znajduje się jednak w parametrach metody host.get:

"groupids": 2

Ograniczamy wynik wyłącznie do hostów należących do grupy o ID równym 2. W naszym przykładzie jest to grupa Linux servers.

"output": ["hostid","host"]

Określamy, które pola obiektu hosta mają zostać zwrócone w odpowiedzi. Skąd wiemy, jakie pola są dostępne i jakie mogą przyjmować wartości? Autor ponownie odsyła do >oficjalnej dokumentacji obiektów zwracanych przez metodę host.get. W naszym przykładzie interesują nas dwa pola – ID hosta (hostid) oraz jego nazwa (host)

"selectInterfaces": ["interfaceid","available","type","error","errors_from"]

Parametr ten wskazuje, że oprócz danych hosta metoda ma zwrócić jeszcze obiekty interfejsów przypisanych do hosta (Szczegółowy opis zwracanych pól również znajduje się w >oficjalnej dokumentacji). W naszym przykładzie pobieramy:

  • interfaceid – id interfejsu hosta
  • available – status interfejsu hosta. Zwracane wartości: 0 – Nieznany (ang. Unknown), 1 – Dostępny (ang. Available), 2 – Niedostępny (ang. Not available)
  • type – typ interfejsu. Zwracane wartości: 1 – Zabbix Agent (pasywny), 2- SNMP, 3 – IMPI, 4 – JMX  
  • error – tekst błędu jeżeli status interfejsu to niedostępny
  • erorrs_from – czas, od kiedy interfejs znajduje się w stanie niedostępnym.
"filter": { "status": 0 }

Dodatkowe filtrowanie wyników – w tym przypadku wybieramy wyłącznie hosty aktywne (pole status = 0).

Po wklejeniu zapytania do sekcji Body Content i uruchomieniu zapytania powinniśmy zobaczyć następujący wynik:

Rysunek 5. Pierwszy wynik zapytania o interfejsy hostów

Na tym etapie widzimy już, że API Zabbix zwraca kompletne, ale dość „surowe” dane. W obecnej postaci trudno je bezpośrednio wykorzystać w panelu Grafany – mamy zagnieżdżone struktury, listę interfejsów wewnątrz obiektów hostów oraz pola, które wymagają dodatkowego przetworzenia np. znaczniki czasu.

To jest moment, w którym UQL zaczyna mieć realne zastosowanie. Z API otrzymujemy komplet danych, ale w formie, która nie nadaje się jeszcze bezpośrednio do wizualizacji. Zamiast rozbudowywać zapytanie po stronie Zabbixa albo przenosić całą logikę filtrowania i obróbki do transformacji w panelu Grafany, wykorzystamy UQL do uporządkowania i przekształcenia odpowiedzi już na etapie zapytania — w sposób czytelny, przewidywalny i łatwy do dalszego rozwijania.

Zaczniemy od prostego, ale bardzo typowego zapytania UQL, którego celem jest:

  • wyciągnięcie właściwego fragmentu odpowiedzi API,
  • „spłaszczenie” listy interfejsów,
  • odfiltrowanie tylko tych interfejsów, które nie są dostępne (Unknown oraz Not available),
  • przygotowanie danych w formie czytelnej tabeli gotowej do wizualizacji.

Nasze pierwsze zapytanie UQL wygląda następująco:

parse-json
| scope "result"
| mv-expand "interfaces"
| extend "error_time"=mul("interfaces.errors_from",1000)
| where "interfaces.available"!='1'
| project "host"="host", "typ"="interfaces.type", "błąd"="interfaces.error", "czas błędu"="error_time", "dostępność"="interfaces.available"

Zatrzymajmy się teraz na chwilę i przeanalizujmy krok po kroku kolejne etapy działania parsera UQL, razem z przykładem pokazującym, co dzieje się z naszym wynikiem na każdym etapie przetwarzania. Dla uproszczenia będziemy pracować tylko na pojedynczym hoście (zabbix-7-0) posiadającym dwa interfejsy. Przykładowa odpowiedź zwrócona przez API wygląda następująco:

{
    "jsonrpc": "2.0",
    "result": [
        {
            "hostid": "10084",
            "host": "zabbix-7-0",
            "interfaces": [
                {
                    "interfaceid": "1",
                    "available": "1",
                    "type": "1",
                    "error": "",
                    "errors_from": "0"
                },
                {
                    "interfaceid": "34",
                    "available": "2",
                    "type": "2",
                    "error": "Cannot connect to \"dns.tajny:161\": Unknown user name.",
                    "errors_from": "1764012994"
                }
            ]
        }
    ],
    "id": 1
}

Kolejne kroki parsera UQL wyglądają następująco:

parse-json

Na tym etapie informujemy parser UQL, że pracujemy na danych w formacie JSON. Dzięki temu dalsze polecenia mogą operować na strukturze obiektów, a nie na surowym tekście.

scope "result"

Zawężamy wynik tylko do obiektu result, pomijając pola id oraz jsonrpc. Po zastosowaniu tego kroku struktura danych wygląda następująco:

[
        {
            "hostid": "10084",
            "host": "zabbix-7-0",
            "interfaces": [
                {
                    "interfaceid": "1",
                    "available": "1",
                    "type": "1",
                    "error": "",
                    "errors_from": "0"
                },
                {
                    "interfaceid": "34",
                    "available": "2",
                    "type": "2",
                    "error": "Cannot connect to \"dns.tajny:161\": Unknown user name.",
                    "errors_from": "1764012994"
                }
            ]
        }
    ]

W Grafanie możemy już zauważyć, że poszczególne pola zostały poprawnie rozpoznane, jednak dane wciąż wymagają dalszej obróbki.

Rysunek 6. Drugi wynik zapytania o interfejsy hostów
mv-expand "interfaces"

To polecenie służy do „rozpakowywania” tablic — dokładnie tak jak w naszym przypadku. Do tej pory każdy host zawierał w sobie tablicę interfaces, czyli wiele interfejsów obrębie pojedynczego rekordu (hosta). Taka struktura nie nadaje się do wizualizacji, ponieważ Grafana nie potrafi pracować na jednym wierszu zawierającym wiele obiektów.

Dzięki mv-expand z jednego obiektu tworzonych jest kilka osobnych rekordów — w naszym przypadku po jednym rekordzie dla każdego interfejsu. Efektem są dwa niezależne wpisy:

[
  {
    "hostid": "10084",
    "host": "zabbix-7-0",
    "interfaces": {
      "interfaceid": "1",
      "available": "1",
      "type": "1",
      "error": "",
      "errors_from": "0"
    }
  },
  {
    "hostid": "10084",
    "host": "zabbix-7-0",
    "interfaces": {
      "interfaceid": "34",
      "available": "2",
      "type": "2",
      "error": "Cannot connect to \"dns.tajny:161\": Unknown user name.",
      "errors_from": "1764012994"
    }
  }
]

Od tego momentu każdy interfejs możemy traktować jako osobny wiersz danych — filtrować go, przekształcać i wybierać interesujące nas pola. Na tym etapie Grafana powinna już prezentować dane w formie oddzielnych rekordów.

Rysunek 7. Trzeci wynik zapytania o interfejsy hostów
extend "error_time"=mul("interfaces.errors_from",1000)

Pole errors_from zwracane przez Zabbix w obiekcie interfejsu zawiera znacznik czasu w sekundach. Grafana operuje na timestampach w milisekundach, dlatego konieczne jest przeliczenie tej wartości.Wykorzystujemy do tego funkcję mul, mnożąc wartość przez 1000. Polecenie extend tworzy nowe pole error_time, zawierające już czas w poprawnym formacie. Przykładowy wynik po tym kroku (dla czytelności pominięto część obiektu interfaces) wygląda następująco:

[
  {
    "hostid": "10084",
    "host": "zabbix-7-0",
    "interfaces": {
      .......
      "errors_from": "0"
    },
    "error_time": 0
  },
  {
    "hostid": "10084",
    "host": "zabbix-7-0",
    "interfaces": {
      ......
      "errors_from": "1764012994"
    },
    "error_time": 1764012994000
  }
]

W Grafanie pojawi się również nowa kolumna z przeliczonym czasem błędu.

Rysunek 8. Czwarty wynik zapytania o interfejsy hostów

where "interfaces.available"!='1'

To polecenie filtruje wynik, pozostawiając wyłącznie te interfejsy, które nie są dostępne. Przypomnijmy: wartość 1 w polu available oznacza interfejs dostępny, dlatego po zastosowaniu filtra otrzymujemy tylko przypadki wymagające uwagi — interfejsy niedostępne (2) lub w stanie nieznanym (0).

W naszym przykładzie wynik po filtracji wygląda następująco:

  {
    "hostid": "10084",
    "host": "zabbix-7-0",
    "interfaces": {
      "interfaceid": "34",
      "available": "2",
      "type": "2",
      "error": "Cannot connect to \"dns.tajny:161\": Unknown user name.",
      "errors_from": "1764012994"
    },
  "error_time": 1764012994000
  }
]

W Grafanie zobaczymy już tylko jeden wiersz odpowiadający niedziałającemu interfejsowi.

Rysunek 9. Piąty wynik zapytania o interfejsy hostów
project "host"="host", "typ"="interfaces.type", "błąd"="interfaces.error", "czas błędu"="error_time", "dostępność"="interfaces.available"

Na końcu układamy wynik w czytelną tabelę, wybierając tylko te pola, które chcemy zaprezentować w panelu, oraz nadając im bardziej zrozumiałe nazwy:

  • host – nazwa hosta z Zabbixa
  • typ – typ interfejsu (agent, SNMP itd.) w postaci wartości numerycznej
  • błąd – treść ostatniego błędu (jeżeli interfejs był niedostępny)
  • czas błędu – moment, od którego interfejs pozostaje niedostępny (timestamp w milisekundach)
  • dostępność – informacja, czy interfejs jest w stanie nieznanym (0) czy niedostępnym (2)

To finalne „posprzątanie” danych — po tym kroku wynik jest gotowy do wyświetlenia w panelu tabelarycznym:

[
  {
    "host": "zabbix-7-0",
    "typ": "2",
    "błąd": "Cannot connect to \"dns.tajny:161\": Unknown user name.",
    "czas błędu": 1764012994000,
    "dostępność": "2"
  }
]

Po wykonaniu wszystkich powyższych kroków otrzymujemy czytelną, uporządkowaną tabelę z danymi, którą możemy bezpośrednio wykorzystać w Grafanie.

Rysunek 10. Ostateczny wynik zapytania o interfejsy hostów

Zróbmy jeszcze kilka dodatkowych kroków, aby nasza tabelka była jeszcze bardziej czytelna.

  • W sekcji Transformations dodajmy transformację o nazwie Convert field type. W jej konfiguracji, w polu Field, wybierzmy kolumnę czas błędu – chcemy, by Grafana traktowała tą kolumnę jako czas, dlatego w polu as wybierzmy Time. Od tego momentu kolumna powinna wyświetlać datę i godzinę zamiast surowego znacznika czasu (timestamp).
Rysunek 11. Transformacja zmieniająca czas błędu na format czasu
  • Ponieważ chcemy prezentować dane w formie tabeli, w sekcji po prawej stronie, w polu Visualization, wybieramy Table. Dodatkowo ustawiamy:
    • tytuł, np. Lista niedostępnych interfejsów
    • opis, np. Lista niedostępnych interfejsów z grupy ‘Linux servers’
Rysunek 12. Podstawowe ustawienia tabeli
  • Chcemy, aby dane były bardziej czytelne dla użytkownika, który nie jest zaznajomiony z API Zabbixa. W tym celu wykorzystamy mechanizm nadpisywania ustawień pól (ang. Field overrides). W tym celu, w sekcji po prawej stronie zjedźmy na sam dół aż znajdziemy przycisk + Add field override:

Rysunek 13. Dodawanie nadpisywania ustawień pola

W naszym przykładzie wybieramy pierwszą opcję Fields with name:

Rysunek 14. Wybranie opcji nadpisywania pola dla konkretnej nazwy

Wskazujemy teraz na pole czas błędu. Teraz dodajemy nowe nadpisanie – wybieramy mapowanie wartości (ang. Value mappings) dla tej konkretnej kolumny:

Rysunek 15. Dodanie nadpisania – mapowanie wartości dla pola “czas błędu”

Dodajmy więc wpisy do mapy wartości poprzez przycisk Add value mappings:

Rysunek 16. Ustawienie mapowania wartości dla pola “czas błędu”

W nowym okienku wpisujemy mapowanie zera (0) na myślnik (-). Dzięki temu Grafana nie będzie wyświetlać daty 01-01-1970 (czyli daty odpowiadającej timestampowi równemu 0), a zamiast tego pokaże po prostu myślnik. Jest to znacznie czytelniejsze i od razu sygnalizuje brak wartości. Na koniec pamiętajmy o kliknięciu Update.

Rysunek 17. Ustawienie mapowania zera na myślnik dla pola “czas błędu”

Bazując na powyższym przykładzie, dodajmy kolejne nadpisanie ustawień — tym razem dla kolumny typ, z odpowiednim mapowaniem wartości. Przypominamy, że są to wartości zwracane bezpośrednio z API Zabbixa dla obiektu interfejsu hosta.

Rysunek 18. Ustawienie nadpisywania ustawień dla pola “typ”

Jeżeli chcemy dodać więcej mapowań, w okienku Value mappings wystarczy kliknąć + Add a new mapping i wybrać odpowiedni typ np. Value dla pojedynczej wartości lub Regex dla wyrażeń regularnych.

Warto dodać jeszcze jedno nadpisanie ustawień dla kolumny dostępność, również z odpowiednim mapowaniem wartości.

Rysunek 19. Ustawienie nadpisywania ustawień dla pola “dostępność”

Kolory możemy ustawić w okienku Value mappings klikając przy odpowiednim mapowaniu na Set color i wybierając interesujący nas kolor dla odpowiedniej wartości:

Rysunek 20. Ustawienie mapowania kolorów dla pola “dostępność”

Zostając jeszcze przy nadpisaniu ustawień pola dostępność – dodajmy jeszcze jedno nadpisanie:

Cell option > Cell type 

Dzięki temu ustawimy kolorowanie komórek na podstawie wartości tego pola. Z listy wybieramy opcję Colored background, a następnie zaznaczamy Apply to entire row. Sprawi to, że cały wiersz tabeli będzie podświetlany odpowiednim kolorem w zależności od statusu interfejsu. Całość nadpisania ustawień dla tego pola powinna wyglądać następująco:

Rysunek 21. Całość ustawień nadpisania dla pola “dostępność”

Ostatnim krokiem jest dostosowanie wyglądu całej tabeli według własnych preferencji. Autor poleca na przykład włączyć filtrowanie po wartościach kolumn, korzystając z opcji Table – Column filter. Jest to szczególnie przydatne, gdy chcemy szybko wyświetlić np. tylko hosty ze statusem „nieznany”.

To wszystko. Możemy wyjść z konfiguracji panelu i pamiętajmy o zapisaniu zmian. Naszym oczom powinna ukazać się tabela z listą niedostępnych interfejsów (w razie potrzeby warto poszerzyć kafelek panelu, jeśli jest zbyt wąski).

Rysunek 22. Lista niedostępnych interfejsów – wynik końcowy

Jeżeli wrócimy teraz do interfejsu Zabbixa i sprawdzimy hosta zabbix-7-0, możemy potwierdzić poprawność danych. Zobaczymy, że dla interfejsów ze statusem Nieznany nie występuje żaden komunikat błędu. Dodatkowo udało nam się pozyskać informację, której nie da się odczytać bezpośrednio z interfejsu Zabbixa — czas niedostępności interfejsu.

Jest to wartość wykorzystywana wewnętrznie przez zabbix-server do zarządzania niedostępnymi interfejsami i nie jest ona dostępna w GUI. Jednak, cytując klasyka: „tylko głupi by nie skorzystał”.

Rysunek 23. Lista niedostępnych interfejsów dla hosta zabbix-7-0 – potwierdzenie działania

Dodatkowe porady

Kilka praktycznych wskazówek, które warto mieć na uwadze podczas pracy z UQL:

  • Pobieraj z API Zabbix tylko to, co naprawdę potrzebne – ograniczaj zapytania do API do niezbędnych pól. Dzięki temu dane będą lżejsze, a zapytania szybsze. W szczególności unikaj używania opcji extend np.:
    "output": "extend"
  • Szukając przykładów w internecie pamiętaj, że UQL to nie KQL i jego składnia nie jest dokładnie taka sama. Niestety, ale oficjalna dokumentacja UQL również nie jest perfekcyjna – Nie wszystkie funkcje, które działają w praktyce, są w niej opisane – np. opisana przez Autora where działa, choć w dokumentacji jej nie znajdziesz. 
  • W wielu miejscach w API Zabbix występują znaczniku czasu (ang. timestamp). Oprócz transformacji wartości (mnożąc ją przez 1000) można użyć wbudowanej funkcji unixtime_seconds_todatetime wraz ze wskazaniem pola np.:
    extend "error_time"=unixtime_seconds_todatetime("interfaces.errors_from")
  • Składnia UQL jest bardzo restrykcyjna – każde odstępstwo może powodować błąd przetwarzania danych. Funkcję musisz używać dokładnie tak, jak opisano w dokumentacji, zwracając szczególną uwagę na spacje, cudzysłowy i nowe linie w zapytaniu. 

W praktyce ten kod jest poprawny:

parse-json
| project "host"="host"  

Natomiast te będą powodować błędy:

  • Brak nowej linii:
    parse-json | project "host"="host"  
  • Za dużo nowych linii:
    parse-json
    | project
    "host"="host" 
  • Dodatkowe spacje:
    parse-json
    | project "host" = "host"

  • Złe cudzysłowia:
    parse-json
    | project "host"='host'
  • Brak cudzysłowów:
    parse-json
    | project host=host
  •  Komentarze:
    parse-json
    | project "host"="host"   # wyswietlamy tylko kolumne host

I tym podobne. Dlatego podczas odpytywania chataGPT pisaniu własnoręcznie zapytań zwróć na to szczególną uwagę. Niestety Infinity nie wskaże nam dokładnie, gdzie leży błąd – w przypadku błędnej składni zwróci jedynie ogólny komunikat o problemie z parsowaniem.

Podsumowanie

W tym artykule pokazaliśmy, jak przy pomocy parsera UQL uporządkować dane zwracane przez API Zabbix i przygotować je do czytelnej wizualizacji w Grafanie. Krok po kroku przeszliśmy przez cały proces — od surowej odpowiedzi API, przez jej oczyszczenie i filtrowanie, aż po gotową tabelę, która faktycznie niesie ze sobą użyteczną informację.

Uważny czytelnik mógł jednak zauważyć, że w przedstawionym przykładzie nie pojawił się status Agenta Zabbix działającego w trybie aktywnym. Jest to temat nieco bardziej złożony, z którym postaramy się zmierzyć w kolejnym artykule poświęconym parserowi JSONata.

Jeżeli artykuł pomógł Ci lepiej zrozumieć, co dzieje się „po drodze” między API a panelem w Grafanie i sprawił Ci tyle przyjemności, co łyk ulubionego napoju, możesz wesprzeć autora równie >dobrą kawką!

Jednocześnie zapraszamy na nasze nowe duże szkolenie Zabbix Expert z ponad 70% rabatem! Zapisy tutaj: https://zabbix.sekurak.pl

~ Albert Przybylski, zawodowo: Architekt ds. Monitoringu w firmie Aplitt, prywatnie: pełnoprawny fanatyk Zabbixa zasilany kawą

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

Odpowiedz