Razvijamo najbolj priročen vmesnik na svetu* za pregledovanje dnevnikov

Razvijamo najbolj priročen vmesnik na svetu* za pregledovanje dnevnikov Če ste že kdaj uporabljali spletne vmesnike za pregledovanje dnevnikov, ste verjetno opazili, da so ti vmesniki praviloma okorni in (pogosto) premalo priročni in odzivni. Na nekatere se lahko navadiš, nekatere so popolnoma grozne, vendar se mi zdi, da je razlog za vse težave v tem, da se naloge ogledovanja dnevnikov lotevamo napačno: poskušamo ustvariti spletni vmesnik, kjer CLI (vmesnik ukazne vrstice) deluje bolje. Osebno zelo dobro delam s programi tail, grep, awk in drugimi, zato bi bil zame idealen vmesnik za delo z dnevniki nekaj podobnega tail in grep, vendar bi ga lahko uporabili tudi za branje dnevnikov, ki prihajajo iz številnih strežnikov. To je, seveda, preberite jih iz ClickHouse!

*po osebnem mnenju uporabnika habre zažigaš

Spoznajte logscli

Nisem si izmislil imena svojega vmesnika in, če sem iskren, obstaja v obliki prototipa, a če želite takoj videti izvorno kodo, ste dobrodošli: https://github.com/YuriyNasretdinov/logscli (350 vrstic izbrane kode Go).

možnosti

Moj cilj je bil narediti vmesnik, ki bi se zdel znan tistim, ki so vajeni tail/grep, to je, da podpira naslednje stvari:

  1. Oglejte si vse dnevnike brez filtriranja.
  2. Pusti vrstice, ki vsebujejo fiksni podniz (zastavica -F у grep).
  3. Pustite vrstice, ki se ujemajo z regularnim izrazom (zastavica -E у grep).
  4. Privzeto je ogled v obratnem kronološkem vrstnem redu, saj so običajno najprej zanimivi najnovejši dnevniki.
  5. Pokaži kontekst poleg vsake vrstice (možnosti -A, -B и -C у grep, s tiskanjem N vrstic pred, za in okoli vsake ujemajoče se vrstice).
  6. Oglejte si dohodne dnevnike v realnem času, s filtriranjem ali brez (v bistvu tail -f | grep).
  7. Vmesnik mora biti združljiv z less, head, tail in drugi - privzeto naj bodo rezultati vrnjeni brez omejitev glede njihovega števila; vrstice se tiskajo kot tok, dokler jih uporabnik zanima; signal SIGPIPE mora tiho prekiniti pretakanje dnevnika, tako kot oni tail, grep in druge pripomočke UNIX.

Реализация

Predvidevam, da že nekako znate dostaviti dnevnike ClickHouse. Če ne, priporočam, da poskusite lsd и kittenhousein ta članek o dostavi dnevnika.

Najprej se morate odločiti za osnovno shemo. Ker običajno želite prejemati dnevnike razvrščene po času, se zdi logično, da jih tako shranite. Če obstaja veliko kategorij dnevnikov in so vse iste vrste, potem lahko naredite kategorijo dnevnika kot prvi stolpec primarnega ključa - to vam bo omogočilo, da imate eno tabelo namesto več, kar bo velika prednost, ko vstavljanje v ClickHouse (na strežnikih s trdimi diski je priporočljivo vnašanje podatkov največ ~1-krat na sekundo za celoten strežnik).

To pomeni, da potrebujemo približno naslednjo shemo tabele:

CREATE TABLE logs(
    category LowCardinality(String), -- категория логов (опционально)
    time DateTime, -- время события
    millis UInt16, -- миллисекунды (могут быть и микросекунды, и т.д.): рекомендуется хранить, если событий много, чтобы было легче различать события между собой
    ..., -- ваши собственные поля, например имя сервера, уровень логирования, и так далее
    message String -- текст сообщения
) ENGINE=MergeTree()
ORDER BY (category, time, millis)

Na žalost nisem mogel takoj najti nobenega odprtega vira z realističnimi dnevniki, ki bi ga lahko zagrabil in prenesel, zato sem to namesto tega vzel za primer ocene izdelkov iz Amazona pred letom 2015. Seveda njihova struktura ni popolnoma enaka strukturi besedilnih dnevnikov, vendar za ilustracijo to ni pomembno.

navodila za nalaganje Amazonovih ocen v ClickHouse

Ustvarimo tabelo:

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 podatkovnem nizu Amazon je samo datum za pregled, ni pa točnega časa, zato izpolnimo te podatke z naključjem.

Ni vam treba prenesti vseh datotek tsv in se omejiti na prvih ~10-20, da bi dobili precej velik nabor podatkov, ki se ne bo prilegal v 16 GB RAM-a. Za nalaganje datotek TSV sem uporabil naslednji ukaz:

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 standardni obstojni disk (ki je HDD) v Google Cloud velikosti 1000 GB (to velikost sem vzel predvsem zato, da je bila hitrost malo višja, čeprav bi bil morda SSD potrebne velikosti cenejši) nalaganje hitrost je bila približno ~ 75 MB/s pri 4 jedrih.

  • Pridržati se moram, da delam v Googlu, vendar sem uporabljal osebni račun in ta članek nima nobene zveze z mojim delom v podjetju

Vse ilustracije bom izdelal s tem naborom podatkov, saj je to vse, kar sem imel pri roki.

Prikaži potek skeniranja podatkov

Ker bomo v ClickHouse uporabili popolno skeniranje tabele z dnevniki in ta operacija lahko traja precej časa in morda dolgo ne bo prinesla rezultatov, če najdemo malo ujemanj, je priporočljivo, da lahko prikažete potek poizvedbe, dokler niso prejete prve vrstice z rezultatom. Če želite to narediti, obstaja parameter v vmesniku HTTP, ki vam omogoča pošiljanje napredka v glavah HTTP: send_progress_in_http_headers=1. Na žalost standardna knjižnica Go ne more brati glav, ko so prejete, vendar vmesnik HTTP 1.0 (ne zamenjujte z 1.1!) podpira ClickHouse, tako da lahko odprete neobdelano povezavo TCP s ClickHouse in jo pošljete tja GET /?query=... HTTP/1.0nn in prejmejo glave in telo odgovora brez ubežanja ali šifriranja, tako da nam v tem primeru sploh ni treba uporabiti standardne knjižnice.

Pretakanje dnevnikov iz ClickHouse

ClickHouse ima relativno dolgo (od leta 2019?) optimizacijo za poizvedbe z ORDER BY, zato poizvedba, kot je

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

Takoj bo začel vračati vrstice, ki imajo v sporočilu podniz "nekaj", ne da bi čakal, da se skeniranje konča.

Prav tako bi bilo zelo priročno, če bi ClickHouse sam preklical zahtevo, ko je bila povezava z njim zaprta, vendar to ni privzeto vedenje. Samodejni preklic zahteve lahko omogočite z možnostjo cancel_http_readonly_queries_on_client_close=1.

Pravilno ravnanje s SIGPIPE v Go

Ko izvršite, recimo, ukaz some_cmd | head -n 10, točno tako ukaz some_cmd ustavi izvajanje, ko head odštel 10 vrstic? Odgovor je preprost: kdaj head konča, se cev zapre in stdout ukaza some_cmd začne pogojno kazati "nikamor". Kdaj some_cmd poskuša pisati v zaprto cev, prejme signal SIGPIPE, ki privzeto tiho prekine program.

V Go se to zgodi tudi privzeto, vendar upravljalnik signalov SIGPIPE na koncu natisne tudi "signal: SIGPIPE" ali podobno sporočilo, in da počistimo to sporočilo, moramo sami ravnati s SIGPIPE tako, kot želimo, to je samo tiho izhod:

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

Pokaži kontekst sporočila

Pogosto želite videti kontekst, v katerem je prišlo do napake (na primer, katera zahteva je povzročila paniko ali katere povezane težave so bile vidne pred zrušitvijo) in v grep To se izvede z možnostmi -A, -B in -C, ki prikazujejo določeno število vrstic za, pred in okoli sporočila.

Na žalost nisem našel enostavnega načina, da bi enako naredil v ClickHouse, zato je za prikaz konteksta v vsako vrstico rezultata poslana dodatna zahteva, kot je ta (podrobnosti so odvisne od razvrščanja in tega, ali je kontekst prikazan pred ali po):

SELECT time,millis,review_body FROM amazon
WHERE (time = 'ВРЕМЯ_СОБЫТИЯ' AND millis < МИЛЛИСЕКУНДЫ_СОБЫТИЯ) OR (time < 'ВРЕМЯ_СОБЫТИЯ')
ORDER BY time DESC, millis DESC
LIMIT КОЛИЧЕСТВО_СТРОК_КОНТЕКСТА
SETTINGS max_threads=1

Ker je zahteva poslana skoraj takoj po tem, ko ClickHouse vrne ustrezno vrstico, konča v predpomnilniku in na splošno se zahteva izvede precej hitro in porabi malo CPE (običajno zahteva traja približno ~6 ms na mojem virtualnem računalniku).

Prikaži nova sporočila v realnem času

Da prikažemo dohodna sporočila v (skoraj) realnem času, preprosto izvedemo zahtevo vsakih nekaj sekund in si zapomnimo zadnji časovni žig, na katerega smo naleteli prej.

Primeri ukazov

Kako so tipični ukazi logscli videti v praksi?

Če ste prenesli nabor podatkov Amazon, ki sem ga omenil na začetku članka, lahko zaženete naslednje ukaze:

# Показать строки, где встречается слово 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

reference

Koda pripomočka (brez dokumentacije) je na voljo na githubu na https://github.com/YuriyNasretdinov/logscli. Z veseljem bom slišal vaše misli o moji ideji o konzolnem vmesniku za pregledovanje dnevnikov, ki temelji na ClickHouse.

Vir: www.habr.com

Dodaj komentar