Razvijamo najpraktičnije sučelje na svijetu* za pregledavanje zapisa

Razvijamo najpraktičnije sučelje na svijetu* za pregledavanje zapisa Ako ste ikad koristili web sučelja za pregled zapisa, onda ste vjerojatno primijetili kako su ta sučelja u pravilu glomazna i (često) ne baš zgodna i osjetljiva. Na neke se možete naviknuti, neki su apsolutno užasni, ali čini mi se da je razlog svih problema to što neispravno pristupamo zadatku pregledavanja zapisa: pokušavamo stvoriti web sučelje gdje CLI (sučelje naredbenog retka) radi bolje. Meni osobno je vrlo ugodno raditi s tail, grep, awk i drugima, pa bi za mene idealno sučelje za rad s zapisima bilo nešto slično tail i grep-u, ali koje bi se također moglo koristiti za čitanje zapisa koji dolaze s mnogih poslužitelja. To je, naravno, pročitajte ih iz ClickHouse!

*prema osobnom mišljenju korisnika habre Ti si zakon

Upoznajte logscli

Nisam smislio ime za svoje sučelje i, da budem iskren, radije postoji u obliku prototipa, ali ako želite odmah vidjeti izvorni kod, onda ste dobrodošli: https://github.com/YuriyNasretdinov/logscli (350 redaka odabranog Go koda).

Sposobnosti

Moj cilj je bio napraviti sučelje koje će djelovati poznato onima koji su navikli na tail/grep, odnosno podržavati sljedeće stvari:

  1. Pogledajte sve zapisnike, bez filtriranja.
  2. Ostavite retke koji sadrže fiksni podniz (zastavica -F у grep).
  3. Ostavite retke koji odgovaraju regularnom izrazu (zastavica -E у grep).
  4. Prema zadanim postavkama, pregled je obrnutim kronološkim redoslijedom, budući da su najnoviji zapisnici obično prvi od interesa.
  5. Prikaži kontekst pored svakog retka (opcije -A, -B и -C у grep, ispisivanjem N redaka prije, iza i oko svakog odgovarajućeg retka).
  6. Pregledajte dolazne zapisnike u stvarnom vremenu, sa ili bez filtriranja (u suštini tail -f | grep).
  7. Sučelje mora biti kompatibilno s less, head, tail i ostali - prema zadanim postavkama, rezultati bi se trebali vratiti bez ograničenja njihovog broja; linije se ispisuju kao tok sve dok je korisnik zainteresiran za njihov prijem; signal SIGPIPE trebali tiho prekinuti strujanje dnevnika, baš kao što to čine tail, grep i druge UNIX pomoćne programe.

Provedba

Pretpostavljam da već nekako znate kako isporučiti zapise ClickHouseu. Ako ne, preporučam isprobati LSD и kućica za mačićeI ovaj članak o isporuci dnevnika.

Prvo morate odlučiti o osnovnoj shemi. Budući da obično želite primati zapise razvrstane po vremenu, čini se logičnim pohraniti ih na taj način. Ako postoji mnogo kategorija dnevnika i sve su iste vrste, tada možete napraviti kategoriju dnevnika kao prvi stupac primarnog ključa - to će vam omogućiti da imate jednu tablicu umjesto nekoliko, što će biti veliki plus kada umetanje u ClickHouse (na poslužiteljima s tvrdim diskovima preporuča se unos podataka ne više od ~1 puta u sekundi za cijeli server).

Odnosno, trebamo otprilike sljedeću shemu tablice:

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

Nažalost, nisam mogao odmah pronaći otvorene izvore s realističnim zapisima koje bih mogao zgrabiti i preuzeti, pa sam ovo uzeo kao primjer recenzije proizvoda iz Amazona prije 2015. Naravno, njihova struktura nije potpuno ista kao kod tekstualnih dnevnika, ali radi ilustracije to nije važno.

upute za učitavanje recenzija Amazona na ClickHouse

Kreirajmo tablicu:

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

U Amazonovom skupu podataka postoji samo datum za recenziju, ali ne postoji točno vrijeme, pa popunimo ove podatke nasumično.

Ne morate preuzeti sve tsv datoteke i ograničiti se na prvih ~10-20 kako biste dobili prilično velik skup podataka koji neće stati u 16 GB RAM-a. Za prijenos TSV datoteka koristio sam sljedeću naredbu:

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 trajni disk (što je HDD) u Google Cloudu veličine 1000 GB (uzeo sam ovu veličinu uglavnom da bi brzina bila malo veća, iako bi možda SSD potrebne veličine bio jeftiniji) upload brzina je bila otprilike ~ 75 MB/s na 4 jezgre.

  • Moram napomenuti da radim u Googleu, ali koristio sam osobni račun i ovaj članak nema nikakve veze s mojim radom u tvrtki

Napravit ću sve ilustracije s ovim određenim skupom podataka, budući da je to sve što sam imao pri ruci.

Prikaži napredak skeniranja podataka

Budući da ćemo u ClickHouseu koristiti potpuno skeniranje na tablici s zapisnicima, a ova operacija može potrajati dosta vremena i možda neće dati nikakve rezultate dulje vrijeme ako se pronađe malo podudaranja, preporučljivo je moći prikazati napredak upita dok se ne prime prvi redci s rezultatom. Da biste to učinili, u HTTP sučelju postoji parametar koji vam omogućuje slanje napretka u HTTP zaglavljima: send_progress_in_http_headers=1. Nažalost, standardna Go knjižnica ne može čitati zaglavlja kako su primljena, ali HTTP 1.0 sučelje (ne smije se brkati s 1.1!) podržava ClickHouse, tako da možete otvoriti neobrađenu TCP vezu na ClickHouse i poslati je tamo GET /?query=... HTTP/1.0nn i primiti zaglavlja i tijelo odgovora bez ikakvog bježanja ili enkripcije, tako da u ovom slučaju čak ne trebamo koristiti standardnu ​​biblioteku.

Streaming zapisa iz ClickHousea

ClickHouse već relativno dugo (od 2019?) ima optimizaciju za upite s ORDER BY, pa upit poput

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

Odmah će početi vraćati retke koji imaju podniz "nešto" u svojoj poruci, bez čekanja da skeniranje završi.

Također, bilo bi vrlo zgodno da sam ClickHouse poništi zahtjev kada se prekine veza s njim, ali to nije zadano ponašanje. Opcijom se može omogućiti automatsko otkazivanje zahtjeva cancel_http_readonly_queries_on_client_close=1.

Ispravno rukovanje SIGPIPE u Go

Kada izvršite, recimo, naredbu some_cmd | head -n 10, točno kako naredba some_cmd zaustavlja izvršenje kada head oduzeto 10 redaka? Odgovor je jednostavan: kada head završava, cijev se zatvara, a stdout naredbe some_cmd počinje usmjeravati, uvjetno, "nigdje". Kada some_cmd pokušava pisati u zatvorenu cijev, prima signal SIGPIPE, koji prema zadanim postavkama tiho prekida program.

U Go-u se to također događa prema zadanim postavkama, ali rukovatelj signala SIGPIPE također ispisuje "signal: SIGPIPE" ili sličnu poruku na kraju, a da bismo izbrisali ovu poruku, samo trebamo sami rukovati SIGPIPE-om onako kako želimo, to jest, samo tiho Izlaz:

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

Prikaži kontekst poruke

Često želite vidjeti kontekst u kojem se dogodila neka pogreška (na primjer, koji je zahtjev izazvao paniku ili koji su povezani problemi bili vidljivi prije rušenja), a u grep To se radi korištenjem opcija -A, -B i -C, koje prikazuju određeni broj redaka iza, prije i oko poruke.

Nažalost, nisam pronašao jednostavan način da to isto učinim u ClickHouseu, tako da se za prikaz konteksta šalje dodatni zahtjev kao što je ovaj u svaki redak rezultata (pojedinosti ovise o sortiranju i je li kontekst prikazan prije ili poslije):

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

Budući da se zahtjev šalje gotovo odmah nakon što ClickHouse vrati odgovarajuću liniju, on završava u predmemoriji i općenito se zahtjev izvršava prilično brzo i troši malo CPU-a (obično zahtjev traje oko ~6 ms na mom virtualnom računalu).

Prikaži nove poruke u stvarnom vremenu

Kako bismo prikazali dolazne poruke u (gotovo) stvarnom vremenu, jednostavno izvršavamo zahtjev jednom svakih nekoliko sekundi, prisjećajući se zadnje vremenske oznake na koju smo prije naišli.

Primjeri naredbi

Kako tipične logscli naredbe izgledaju u praksi?

Ako ste preuzeli skup podataka Amazona koji sam spomenuo na početku članka, možete pokrenuti sljedeće naredbe:

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

Kôd pomoćnog programa (bez dokumentacije) dostupan je na githubu na https://github.com/YuriyNasretdinov/logscli. Bilo bi mi drago čuti vaše mišljenje o mojoj ideji za sučelje konzole za pregledavanje zapisa temeljeno na ClickHouseu.

Izvor: www.habr.com

Dodajte komentar