Commit 2f8e0de6 authored by Martin Vítek's avatar Martin Vítek

Přidán článek Vlastní plugin pro netdata

parent 689016d2
Pipeline #712 passed with stage
in 4 minutes and 16 seconds
......@@ -2,7 +2,7 @@
title = "netdata"
perex = "Netdata je skvělý program, který slouží k monitorovnání Vašeho serveru v reálném čase, ve Vašem prohlížeči s minimem nastavování tak, že už nebudete chtít nic jiného."
tags = ["Články","Programování","Nástroje"]
tags = ["Články","Programování","Nástroje","netdata"]
image = "lead.jpg"
lang = "bash"
......
```
title = "Vlastní plugin pro netdata"
tags = ["Programování","netdata"]
image = "lead.jpg"
```
O nástroji pro monitorování serveru v reálném čase jsem již psal v článku o [netdata](clanek/2016/netdata). Dnes budu popisovat, jak si můžete naprogramovat vlastní plugin, který Vám bude v netdata vykreslovat například teplotu a vlhkost z čidla, které máte připojeno k Raspberry.
![Graf napětí a teloty](graf.png =700x)
Plugin je možné naprogramovat v jakémkoliv jazyce, pokud výsledný program dokáže vypisovat na standardní výstup (*stdout*). Dále je možné využít modulární pluginy pro BASH, Python a Node.js, které mají unadnit nastavení a vykreslování grafů.
API netdata je popsáno na [wiki projektu](https://github.com/firehol/netdata/wiki/External-Plugins) a myslím si, že je popsáno ne úplně šťastným způsobem. Při programování mého pluginu jsem tedy hlavně zkoumal, co do netdata posílají ostatní pluginy, které jsou umístěné v:
- Standardní - `/usr/libexec/netdata/plugins.d/`
- Bash - `/usr/libexec/netdata/charts.d/`
- Python - `/usr/libexec/netdata/python.d/`
- Node.js - `/usr/libexec/netdata/node.d/`
<br>
Pro svůj plugin jsem využil standardní API netdata a programovací jazyk C++. Budu tedy popisovat hlavně mé zkušenosti s touto kombinací.
# API netdata
Standardní pluginy, které využívají API netdata, fungují tak, že netdata při startu spouštějí všechny spustitelné soubory, které se nacházejí v `/usr/libexec/netdata/plugins.d/` a předávají jim jeden parametr, který říká, po kolika sekundách má plugin aktualizovat data. Je tedy také nutné počítat s tím, že se pluginy spouští pod uživatelem netdata, který nemusí mít všechna potřebná práva. Plugin po spuštění nastaví grafy, do kterých chce posílat data a poté už v pravidelných intervalech odesílá naměřené hodnoty.
Komunikace pluginu s netdata probíhá přes standardní výstup `stdout`. Zpráva se skládá z úvodního klíčového slova (**CHART, DIMENSION, SET, ...**), dále následují parametry a zpráva je ukončena znakem nového řádku `\n`.
Plugin může vypisovat chyby na chybový výstup `stderr`, který je přesměrován do logu netdata, pokud je logování povoleno.
Parametry je nutno uvádět do jednoduchých uvozovek `'parametr'`, pokud obsají nepovolené znaky (mezera) a pokud je parametr vynechán, tak se odešlou jen prázdné uvozovky `''`. Parametry mohou obsahovat znaky s diakritikou, ale v netdata se nezobrazí správně.
## Nastavení grafu
Nejprve se musí vytvořit graf, do kterého se poté vykreslují data. K tomu slouží zpráva **CHART**, která má následující formát:
```
CHART type.id name title units [family [context [charttype [priority [update_every]]]]]
```
**type.id**<br>
Unikátní název grafu, na který se dále odkazuje z dalších příkazů. `type` označuje kategorii v menu.
**name**<br>
Je použito pro komunikaci s uživatelem jako název grafu. Pokud není vyplněno, tak se použije **id**.
**title**<br>
Popis, který se zobrazuje nad grafem.
**units**<br>
Jednotky hodnot, které jsou vykresleny v grafu.
**family**<br>
Slouží pro seskupování grafů pod submenu v grafu. Všechny grafy, které vykreslují teplotu by měly mít **family** nastaveno například na `'Teplota'`.
**context**<br>
Seskupuje grafy, které mají stejný **context**, aby měly stejný vizuální styl.
**charttype**<br>
Nastavuje typ grafu. Možné hodnoty jsou *line*, *area*, *stacked*. Pokud chybí, tak je použit typ *line*.
**priority**<br>
Udává pozici v menu. Menší číslo znamená větší prioritu a grafy se tak zobrazí výše na stránce. 10000 nastavuje prioritu tak, že se grafy zobrazí pod grafy využití procesoru.
**update_every**<br>
Přepisuje periodu aktualizace, která je nastavena serverem. Měla by odpovídat periodě, s kterou se doopravdy odesílají data.
### **Příklad**
Nastavení grafů, pro zobrazení venkonví i vnitřní teploty a vlhkosti by mohlo vypadat následovně:
```
CHART Meteostanice.temperature_outside 'Venkovni teplota' '°C' teploty '' line 10000 1
CHART Meteostanice.temperature_inside 'Vnitrni teplota' '°C' teploty '' line 10001 1
CHART Meteostanice.humidity 'Vlhkost' '%' vlhkosti '' line 10002 1
```
## Nastavení dimenzí
Pro každý graf se musí nastavit dimenze (data), které se v něm zobrazují a graf jich může mít více. K tomu slouží zpráva **DIMENSION** s formátem:
```
DIMENSION id [name [algorithm [multiplier [divisor [hidden]]]]]
```
Nastavení dimenzí musí proběhnout hned po nadefinování konkrétního grafu, do kterého mají nastavované dimenze patřit.
**id**<br>
Jméno dimenze, na které se odkazuje při odesílání dat. Nemělo by obsahovat znak `.`.
**name**<br>
Jméno dimenze, které se zobrazuje uživateli.
**algorithm**<br>
Nastavuje algoritmus pro zpracování přijatých dat. Může být například *absolute*, kdy se data zobrazují tak, jak byla přijata, nebo například *incremental*, kdy se k zobrazované hodnotě přičítají přijatá data.
**multiplier**<br>
Hodnota (celé číslo), kterou se násobí přijatá data.
**divisor**<br>
Hodnota (celé číslo), kterým se dělí přijatá data.
**hidden**<br>
Klíčové slovo *hidden* schová dimenzi, ale ta bude stále využita při výpočtech ostatních.
### **Příklad**
Grafy teploty mají jen jednu dimenzi, graf vlhkosti má dimenze dvě.
```
DIMENSION temperature_outside Teplota absolute 1 100
DIMENSION temperature_inside Teplota absolute 1 100
DIMENSION humidity_SHT15 SHT15 absolute 1 100
DIMENSION humidity_SHT25 SHT25 absolute 1 100
```
## Odesílání dat
Pro odesílání naměřených dat slouží trojice zpráv *BEGIN*, *SET* a *END*.
Začátek dat označuje *BEGIN* s formátem:
```
BEGIN type.id [microseconds]
```
**type.id**<br>
Udává, do kterého grafu patří odesílaná data.
**microseconds**<br>
Udává počet mikrosekund, který uplynul od poslední aktualizace grafu. Je to nepovinná hodnota, která zpřesní vykreslení dat, pokud by na velmi vytíženém systému docházelo ke zpomalování přenosu dat.
Jednotlivá data jsou uvozena zprávou *SET* s formátem:
```
SET id = value
```
**id**<br>
ID dimenze, do které patří data, která udává value. Netdata přijímají jen celá čísla a pokud se mají zobrazovat desetinná, tak je potřeba data před odesláním vynásobit a nastavit **divisor** na odpovídající hodnotu.
Konec dat je singnalizován zprávou *END*, která nemá žádné parametry.
### **Příklad**
Odeslání naměřených teplot a vlhkostí.
```
BEGIN Meteostanice.temperature_outside
SET temperature_outside = 2055
END
BEGIN Meteostanice.temperature_inside
SET temperature_inside = 2178
END
BEGIN Meteostanice.humidity
SET humidity_SHT15 = 8012
SET humidity_SHT25 = 7982
END
```
## Shrnutí
Pro nastavení grafů meteostanice a následné odesílání dat slouží následující příkazy, v daném pořadí, které náš plugin vypisuje na `stdout`.
```
#Inicializace grafů
CHART Meteostanice.temperature_outside 'Venkovni teplota' '°C' teploty '' line 1000 1
DIMENSION temperature_outside Teplota absolute 1 100
CHART Meteostanice.temperature_inside 'Vnitrni teplota' '°C' teploty '' line 1001 1
DIMENSION temperature_inside Teplota absolute 1 100
CHART Meteostanice.humidity 'Vlhkost' '%' vlhkosti '' line 1002 1
DIMENSION humidity_SHT15 SHT15 absolute 1 100
DIMENSION humidity_SHT25 SHT25 absolute 1 100
#Odesílání dat
BEGIN Meteostanice.temperature_outside
SET temperature_outside = 2055
END
BEGIN Meteostanice.temperature_inside
SET temperature_inside = 2178
END
BEGIN Meteostanice.humidity
SET humidity_SHT15 = 8012
SET humidity_SHT25 = 7982
END
```
Výledek bude vypadat takto:
![Grafy meteostanice](grafy_meteostanice.png =700x)
# Debugování
Pro debugování pluginu ho stačí pouze spustit a jelikož vypisuje na standardní výstup, tak je kontrola velmi snadná.
Pro kontrolu, že plugin běží správně, když je spuštěn přes netdata, lze použít následující postup:
```bash
#Získání PID procesu, pod kterým plugin běží
ps ax | grep muj.plugin
10537 ? R 0:27 /usr/libexec/netdata/plugins.d/muj.plugin 2
#Připojení se na stdout tohoto procesoru
sudo strace -p10537 -s9999 -e write
```
# C++ problém s std::cout
Když jsem programval svůj plugin, tak jsem narazil na problém, kdy se v grafech odesílané hodnoty chvíly zobrazovaly a chvíli se naopak nezobrazovaly, ale při testování mi plugin korektně každou 1s vypisoval odesílání dat. Problém jsem vyřešil poté, co jsem se podíval na výstup pluginu, když byl spuštěn přes netdata. Zde jsem viděl, že se data neodesílají každou vteřinu, ale odeslalo se jich naráz několik za několik sekund. Problém byl v tom, že data vypisuji následovně:
```c++
cout << "BEGIN Stojan.temperatures" << '\n';
cout << "SET RPi = " << temperatures.RPi*100 << '\n';
cout << "SET Interface = " << temperatures.interface*100 << '\n';
cout << "END" << '\n';
```
A výstup na `cout` se provádí z bufferu a není zaručeno, že se výpis provede hned. Jedno řešení je místo `\n` používat `std::endl`, který pošle znak nového řádku a vyprázdní buffer. Nevýhodou tohoto řešení je, že vyprazdňování bufferu zpomaluje program. Druhou možností, kterou jsem využil, je zavolání `std::cout << std::flush;` na konci odesílání dat, čímž dojde k vyprázdnění bufferu a okamžitému výpisu dat na obrazovku.
# Dejte nám vědět!
Naprogramovali jste si vlastní plugin pro netdata? Pochlubte se na na našem [fórum](http://forum.ok1kvk.cz/).
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment