Ak ste niekedy používali webové rozhrania na prezeranie protokolov, pravdepodobne ste si všimli, že tieto rozhrania sú spravidla ťažkopádne a (často) nie príliš pohodlné a citlivé. Na niektoré sa dá zvyknúť, niektoré sú úplne hrozné, ale zdá sa mi, že dôvodom všetkých problémov je, že nesprávne pristupujeme k úlohe prezerania protokolov: snažíme sa vytvoriť webové rozhranie, v ktorom je CLI (rozhranie príkazového riadku) funguje lepšie. Mne osobne veľmi vyhovuje práca s tail, grep, awk a ďalšími, a preto by pre mňa bolo ideálne rozhranie pre prácu s logami niečo podobné ako tail a grep, ktoré by sa však dalo použiť aj na čítanie logov, ktoré prišli z mnohých serverov. To je, samozrejme, prečítajte si ich z ClickHouse!
*podľa osobného názoru používateľa habra
Zoznámte sa s logscli
Názov pre svoje rozhranie som nevymyslel a, úprimne povedané, existuje skôr vo forme prototypu, ale ak chcete okamžite vidieť zdrojový kód, ste vítaní:
Možnosti
Mojím cieľom bolo vytvoriť rozhranie, ktoré by sa zdalo známe tým, ktorí sú zvyknutí na tail/grep, teda na podporu nasledujúcich vecí:
- Zobraziť všetky denníky bez filtrovania.
- Ponechajte riadky obsahujúce pevný podreťazec (príznak
-F
уgrep
). - Nechajte riadky, ktoré zodpovedajú regulárnemu výrazu (príznak
-E
уgrep
). - V predvolenom nastavení je prezeranie v obrátenom chronologickom poradí, pretože ako prvé sú zvyčajne zaujímavé najnovšie protokoly.
- Zobraziť kontext vedľa každého riadku (možnosti
-A
,-B
и-C
уgrep
tlač N riadkov pred, za a okolo každého zodpovedajúceho riadku). - Prezerajte si prichádzajúce denníky v reálnom čase, s filtrovaním alebo bez filtrovania (v podstate
tail -f | grep
). - Rozhranie musí byť kompatibilné s
less
,head
,tail
a iné - štandardne by sa výsledky mali vracať bez obmedzenia ich počtu; riadky sa tlačia ako prúd, pokiaľ má používateľ záujem o ich príjem; signálSIGPIPE
by mali ticho prerušiť streamovanie denníka, rovnako ako onitail
,grep
a ďalšie nástroje UNIX.
Реализация
Budem predpokladať, že už nejako viete, ako doručiť protokoly do ClickHouse. Ak nie, odporúčam vyskúšať
Najprv sa musíte rozhodnúť pre základnú schému. Keďže zvyčajne chcete dostávať protokoly zoradené podľa času, zdá sa logické ich uchovávať týmto spôsobom. Ak existuje veľa kategórií denníkov a všetky sú rovnakého typu, potom môžete vytvoriť kategóriu denníka ako prvý stĺpec primárneho kľúča – to vám umožní mať jednu tabuľku namiesto niekoľkých, čo bude veľké plus, keď vkladanie do ClickHouse (na serveroch s pevnými diskami sa odporúča vkladať dáta nie viac ako ~1-krát za sekundu pre celý server).
To znamená, že potrebujeme približne nasledujúcu schému tabuľky:
CREATE TABLE logs(
category LowCardinality(String), -- категория логов (опционально)
time DateTime, -- время события
millis UInt16, -- миллисекунды (могут быть и микросекунды, и т.д.): рекомендуется хранить, если событий много, чтобы было легче различать события между собой
..., -- ваши собственные поля, например имя сервера, уровень логирования, и так далее
message String -- текст сообщения
) ENGINE=MergeTree()
ORDER BY (category, time, millis)
Žiaľ, nemohol som okamžite nájsť žiadne otvorené zdroje s realistickými protokolmi, ktoré by som mohol chytiť a stiahnuť, takže som namiesto toho vzal toto ako príklad
pokyny na nahrávanie recenzií Amazonu do ClickHouse
Vytvorme si tabuľku:
CREATE TABLE amazon(
review_date Date,
time DateTime DEFAULT toDateTime(toUInt32(review_date) * 86400 + rand() % 86400),
millis UInt16 DEFAULT rand() % 1000,
marketplace LowCardinality(String),
customer_id Int64,
review_id String,
product_id LowCardinality(String),
product_parent Int64,
product_title String,
product_category LowCardinality(String),
star_rating UInt8,
helpful_votes UInt32,
total_votes UInt32,
vine FixedString(1),
verified_purchase FixedString(1),
review_headline String,
review_body String
)
ENGINE=MergeTree()
ORDER BY (time, millis)
SETTINGS index_granularity=8192
V súbore údajov Amazonu je len dátum na kontrolu, ale nie je tam presný čas, takže vyplňte tieto údaje náhodne.
Nemusíte sťahovať všetky súbory tsv a obmedzovať sa na prvých ~10-20, aby ste získali pomerne veľký súbor údajov, ktoré sa nezmestia do 16 GB pamäte RAM. Na nahranie súborov TSV som použil nasledujúci príkaz:
for i in *.tsv; do
echo $i;
tail -n +2 $i | pv |
clickhouse-client --input_format_allow_errors_ratio 0.5 --query='INSERT INTO amazon(marketplace,customer_id,review_id,product_id,product_parent,product_title,product_category,star_rating,helpful_votes,total_votes,vine,verified_purchase,review_headline,review_body,review_date) FORMAT TabSeparated'
done
Na štandardný perzistentný disk (čo je HDD) v Google Cloud s veľkosťou 1000 GB (túto veľkosť som bral hlavne preto, aby bola rýchlosť trochu vyššia, aj keď možno SSD požadovanej veľkosti by bolo lacnejšie) upload rýchlosť bola približne ~ 75 MB/s na 4 jadrách.
- Musím si rezervovať, že pracujem v Google, ale použil som osobný účet a tento článok nemá nič spoločné s mojou prácou v spoločnosti
Vyrobím všetky ilustrácie s týmto konkrétnym súborom údajov, pretože toto je všetko, čo som mal po ruke.
Zobraziť priebeh skenovania údajov
Keďže v ClickHouse použijeme úplné skenovanie tabuľky s protokolmi a táto operácia môže trvať veľa času a nemusí dlho prinášať žiadne výsledky, ak sa nájde málo zhôd, odporúča sa mať možnosť zobraziť priebeh dotazu až do prijatia prvých riadkov s výsledkom. Na tento účel je v rozhraní HTTP parameter, ktorý vám umožňuje odosielať priebeh v hlavičkách HTTP: send_progress_in_http_headers=1
. Žiaľ, štandardná knižnica Go nedokáže čítať hlavičky tak, ako sú prijímané, ale rozhranie HTTP 1.0 (nezamieňať s 1.1!) je podporované službou ClickHouse, takže môžete otvoriť surové pripojenie TCP do ClickHouse a odoslať ho tam. GET /?query=... HTTP/1.0nn
a prijímať hlavičky a telo odpovede bez akéhokoľvek escapovania alebo šifrovania, takže v tomto prípade ani nemusíme používať štandardnú knižnicu.
Streamovanie protokolov z ClickHouse
ClickHouse má optimalizáciu pre dopyty s ORDER BY relatívne dlho (od roku 2019?), takže dopyt ako
SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESC
Okamžite začne vracať riadky, ktoré majú v správe podreťazec „niečo“, bez čakania na dokončenie skenovania.
Tiež by bolo veľmi výhodné, keby ClickHouse sám zrušil požiadavku, keď bolo pripojenie k nemu uzavreté, ale toto nie je predvolené správanie. Pomocou voľby je možné povoliť automatické zrušenie požiadavky cancel_http_readonly_queries_on_client_close=1
.
Správna manipulácia so SIGPIPE v režime Go
Keď vykonáte, povedzme, príkaz some_cmd | head -n 10
, presne ako príkaz some_cmd
zastaví vykonávanie, keď head
odčítal 10 riadkov? Odpoveď je jednoduchá: kedy head
skončí, potrubie sa uzavrie a stdout príkazu some_cmd začne podmienečne ukazovať „nikam“. Kedy some_cmd
pokúša sa písať do uzavretej trubice,
V Go sa to tiež deje štandardne, ale obslužný program signálu SIGPIPE tiež na konci vypíše „signál: SIGPIPE“ alebo podobnú správu a na vymazanie tejto správy stačí, aby sme si so SIGPIPE sami zaobchádzali tak, ako chceme, teda len potichu. VÝCHOD:
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGPIPE)
go func() {
<-ch
os.Exit(0)
}()
Zobraziť kontext správy
Často chcete vidieť kontext, v ktorom sa vyskytla nejaká chyba (napríklad, ktorá požiadavka spôsobila paniku alebo aké súvisiace problémy boli viditeľné pred pádom) a grep
To sa vykonáva pomocou volieb -A, -B a -C, ktoré zobrazujú zadaný počet riadkov za, pred a okolo správy.
Bohužiaľ som nenašiel jednoduchý spôsob, ako urobiť to isté v ClickHouse, takže na zobrazenie kontextu sa na každý riadok výsledku odošle dodatočná požiadavka, ako je táto (podrobnosti závisia od zoradenia a od toho, či je kontext zobrazený pred alebo potom):
SELECT time,millis,review_body FROM amazon
WHERE (time = 'ВРЕМЯ_СОБЫТИЯ' AND millis < МИЛЛИСЕКУНДЫ_СОБЫТИЯ) OR (time < 'ВРЕМЯ_СОБЫТИЯ')
ORDER BY time DESC, millis DESC
LIMIT КОЛИЧЕСТВО_СТРОК_КОНТЕКСТА
SETTINGS max_threads=1
Keďže požiadavka je odoslaná takmer okamžite po tom, čo ClickHouse vráti príslušný riadok, skončí vo vyrovnávacej pamäti a vo všeobecnosti je požiadavka vykonaná pomerne rýchlo a spotrebuje trochu CPU (zvyčajne žiadosť trvá asi ~6 ms na mojom virtuálnom počítači).
Zobrazte nové správy v reálnom čase
Aby sme mohli zobraziť prichádzajúce správy v (takmer) reálnom čase, jednoducho vykonáme požiadavku každých pár sekúnd, pričom si pamätáme poslednú časovú pečiatku, s ktorou sme sa predtým stretli.
Príklady príkazov
Ako vyzerajú typické príkazy logscli v praxi?
Ak ste si stiahli súbor údajov Amazon, ktorý som spomenul na začiatku článku, môžete spustiť nasledujúce príkazy:
# Показать строки, где встречается слово walmart
$ logscli -F 'walmart' | less
# Показать самые свежие 10 строк, где встречается "terrible"
$ logscli -F terrible -limit 10
# То же самое без -limit:
$ logscli -F terrible | head -n 10
# Показать все строки, подходящие под /times [0-9]/, написанные для vine и у которых высокий рейтинг
$ logscli -E 'times [0-9]' -where="vine='Y' AND star_rating>4" | less
# Показать все строки со словом "panic" и 3 строки контекста вокруг
$ logscli -F 'panic' -C 3 | less
# Непрерывно показывать новые строки со словом "5-star"
$ logscli -F '5-star' -tailf
referencie
Pomocný kód (bez dokumentácie) je dostupný na github na adrese
Zdroj: hab.com