Vyvíjíme nejpohodlnější rozhraní na světě* pro prohlížení logů

Vyvíjíme nejpohodlnější rozhraní na světě* pro prohlížení logů Pokud jste někdy používali webové rozhraní pro prohlížení logů, jistě jste si všimli, jak jsou obvykle tyto rozhraní objemná a (často) notně nepohodlná a neodpovídající. Na některé si můžete zvyknout, jiné jsou naprosto hrozné, ale podle mě je příčinou všech problémů to, že k úkolu prohlížení logů přistupujeme špatně: snažíme se vytvořit webové rozhraní tam, kde lépe funguje CLI (příkazová řádka). Přiznám se, že pro mě je velmi pohodlné pracovat s tail, grep, awk a ostatními, a proto by pro mě ideálním rozhraním pro práci s logy bylo něco podobného jako tail a grep, ale které by navíc bylo použitelné pro čtení logů, které přišly z mnoha serverů. To znamená, samozřejmě, číst je z ClickHouse!

*podle osobního názoru uživatele Habr youROCK

Představujeme logscli

Název pro své rozhraní jsem si nevymyslel a pravda je, že existuje spíše ve formě prototypu, ale pokud chcete ihned vidět zdrojové kódy, vítejte: https://github.com/YuriyNasretdinov/logscli (350 řádků vybraného kódu v Go).

Funkce

Cílem bylo vytvořit rozhraní, které bude známé těm, kteří jsou zvyklí na tail/grep, tedy podporující následující funkce:

  1. Prohlížení všech logů, bez filtrování.
  2. Zachovat řádky obsahující daný podřetězec (přepínač -F u grep).
  3. Zachovat řádky vyhovující regulárnímu výrazu (přepínač -E u grep).
  4. Standardně prohlížení v obráceném chronologickém pořadí, protože obvykle jsou nejzajímavější nejnovější logy.
  5. Zobrazení kontextu okolo každého řádku (parametry -A, -B a -C u grep, které tisknou N řádků před, po a kolem každého shodného řádku).
  6. Prohlížení přicházejících logů v reálném čase, s filtrováním i bez (v podstatě tail -f | grep).
  7. Rozhraní by mělo být kompatibilní s less, head, tail a dalšími — standardně by měly být výsledky vráceny bez omezení jejich počtu; řádky se tisknou streamově, dokud má uživatel zájem je přijímat; signál SIGPIPE by měl tiše přerušit streaming logů, stejně jako to dělají tail, grep a další UNIXové utility.

Implementace

Předpokládám, že už víte, jakým způsobem doručit logy do ClickHouse. Pokud ne, doporučuji vyzkoušet lsd a kittenhouse, a také tento článek o doručování logů.

Nejprve je nutné rozhodnout se o schématu databáze. Protože logy obvykle chceme mít seřazené podle času, zdá se logické je takto také uchovávat. Pokud je kategorií logů mnoho a všechny jsou podobné, může být jako první sloupec primárního klíče použita kategorie logů — to umožní mít jednu tabulku místo několika, což při vkládání do ClickHouse bude velkým přínosem (na serverech s pevným diskem se doporučuje vkládat data maximálně ~1 krát za sekundu. pro celý server).

To znamená, že potřebujeme přibližně následující schéma tabulek:

CREATE TABLE logs(
    category LowCardinality(String), -- kategorie logů (volitelně)
    time DateTime, -- čas události
    millis UInt16, -- milisekundy (mohou být i mikrosekundy atd.): doporučuje se uchovávat, pokud je událostí hodně, aby bylo snazší rozlišovat události mezi sebou
    ..., -- vaše vlastní pole, například jméno serveru, úroveň logování atd.
    message String -- text zprávy
) ENGINE=MergeTree()
ORDER BY (category, time, millis)

Bohužel jsem hned nenašel žádné otevřené zdroje s realistickými logy, které by se daly stáhnout, proto jsem místo toho vzal pro příklad recenze produktů z Amazonu do roku 2015. Jejich struktura rozhodně není úplně stejná jako u textových logů, ale pro ilustraci to není zásadní.

návod na importování recenzí z Amazonu do ClickHouse

Vytvoříme tabulku:

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 datasetu Amazonu je pouze datum pro recenzi, ale není zde přesný čas, takže tyto údaje vyplníme náhodně.

Není nutné stahovat všechny tsv soubory, stačí se omezit na prvních ~10-20, abychom už získali dostatečně velkou datovou sadu, která se nevejde do 16 GB RAM. Pro importování TSV souborů jsem použil následující pří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 standardním Persistent Discu (HDD) v Google Cloud o velikosti 1000 GB (tato velikost byla vybrána hlavně proto, aby byla rychlost o něco vyšší, i když by SSD s požadovanou kapacitou pravděpodobně vyšlo levněji) byla rychlost uploadu přibližně 75 MB/s na 4 jádrech.

  • Musím zdůraznit, že pracuji v Googlu, ale používal jsem osobní účet a tento článek nemá žádnou souvislost s mojí prací ve společnosti.

Všechny ilustrace budu vyrábět právě s tímto datovým souborem, protože to je vše, co jsem měl po ruce.

Zobrazení pokroku skenování dat

Jelikož v ClickHouse budeme používat full scan na tabulce s logy, a tato operace může zabrat značný čas a dlouho nevracet žádné výsledky, pokud je shod málo, je žádoucí umět ukazovat pokrok vykonávání dotazu před obdržením prvních řádků s výsledkem. K tomu je v HTTP rozhraní parametr, který umožňuje vracet pokrok v HTTP hlavičkách: send_progress_in_http_headers=1. Bohužel standardní knihovna Go neumí číst hlavičky postupně, ale rozhraní HTTP 1.0 (nemějte to prosím za 1.1!) je podporováno ClickHouse, takže lze otevřít syrové TCP připojení s ClickHousem, poslat tam GET /?query=... HTTP/1.0nn a obdržet v odpovědi hlavičky a tělo odpovědi bez jakéhokoli escapeování a šifrování, takže v tomto případě ani nepotřebujeme standardní knihovnu.

Streamování logů z ClickHouse

V ClickHouse již relativně dlouho (od roku 2019?) existuje optimalizace pro dotazy s ORDER BY, takže dotaz ve formátu

SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESC

začne okamžitě vracet řádky, které obsahují podřetězec "something", aniž by čekal na dokončení skenování.

Také by bylo velmi užitečné, kdyby ClickHouse automaticky zrušil dotaz, když bylo uzavřeno spojení, ale to není výchozí chování. Automatické zrušení dotazu lze zapnout pomocí volby cancel_http_readonly_queries_on_client_close=1.

Správné zpracování SIGPIPE v Go

Když vykonáváte například příkaz some_cmd | head -n 10, jakým způsobem příkaz some_cmd ukončuje své vykonávání, když head přečetl 10 řádků? Odpověď je jednoduchá: když head se ukončí, pipe se zavře a stdout příkazu some_cmd začne ukazovat, řekněme, "nikam". Když some_cmd se pokusí zapsat do uzavřeného pipe, dostane signál SIGPIPE, který ve výchozím nastavení tiše ukončuje program..

V Go se to také děje ve výchozím nastavení, ale obslužný program pro signál SIGPIPE na konci také vypisuje "signal: SIGPIPE" nebo podobnou zprávu, a abychom tuto zprávu odstranili, musíme jednoduchým způsobem zpracovat SIGPIPE podle našich představ, tedy prostě tiše ukončit:

ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGPIPE)
go func() {
    <-ch
    os.Exit(0)
}()

Zobrazení kontextu zprávy

Často chceme vidět kontext, ve kterém došlo k nějaké chybě (například který dotaz způsobil paniku nebo jaké související problémy byly viditelné před pádem), a za tímto účelem grep slouží možnosti -A, -B a -C, které ukazují daný počet řádků po, před a kolem zprávy.

Bohužel jsem nenašel jednoduchý způsob, jak to udělat stejně v ClickHouse, proto pro zobrazení kontextu je na každé řádku výsledku poslán další dotaz přibližně následujícího znění (podrobnosti závisí na řazení a na tom, zda se zobrazuje kontext před nebo po):

SELECT time,millis,review_body FROM amazon
WHERE (time = 'ČAS_UDALOSTI' AND millis < MILISEKUNDY_UDALOSTI) OR (time < 'ČAS_UDALOSTI')
ORDER BY time DESC, millis DESC
LIMIT POČET ŘÁDKŮ_KONTEXTU
SETTINGS max_threads=1

Jelikož je dotaz odeslán téměř okamžitě po tom, co ClickHouse vrátil odpovídající řádek, dostává se do cache a celkově je dotaz prováděn dost rychle a spotřebovává jen trochu CPU (obvykle trvá dotaz kolem ~6 ms na mé virtuální mašině).

Zobrazení nových zpráv v reálném čase

Abychom zobrazili přicházející zprávy v (téměř) reálném čase, jednoduše spouštíme dotaz každých několik sekund, zapamatujeme si poslední časové razítko, které jsme předtím potkali.

Příklady příkazů

Jak vypadají typické příkazy logscli v praxi?

Pokud jste načetli datový soubor Amazon, o kterém jsem zmínil v úvodu článku, můžete provést následující pří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

Odkazy

Kód utility (bez dokumentace) je dostupný na githubu na adrese https://github.com/YuriyNasretdinov/logscli. Budu rád, když uslyším vaše názory na mou myšlenku pro konzolové rozhraní pro prohlížení logů na základě ClickHouse.

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster