Razvijamo najpogodniji interfejs na svijetu* za pregled dnevnika

Razvijamo najpogodniji interfejs na svijetu* za pregled dnevnika Ako ste ikada koristili web interfejse za pregled logova, onda ste verovatno primetili kako su, po pravilu, ovi interfejsi glomazni i (često) ne baš zgodni i brzi. Na neke se možete naviknuti, na neke su apsolutno užasne, ali čini mi se da je razlog svih problema to što zadatku pregledavanja dnevnika pristupamo pogrešno: pokušavamo napraviti web sučelje u kojem će CLI (sučelje komandne linije) radi bolje. Meni lično je vrlo ugodno raditi sa tail, grep, awk i ostalima, pa bi za mene idealno sučelje za rad sa logovima bilo nešto slično tail i grep, ali bi se moglo koristiti i za čitanje logova koji dolaze sa mnogih servera. To je, naravno, pročitajte ih iz ClickHouse-a!

*prema ličnom mišljenju korisnika habra yourROCK

Upoznajte logscli

Nisam smislio naziv 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 linija odabranog Go koda).

Karakteristike

Moj cilj je bio da napravim interfejs koji će izgledati poznato onima koji su navikli na tail/grep, odnosno da podržava sledeće stvari:

  1. Pogledajte sve zapise, bez filtriranja.
  2. Ostavite redove koji sadrže fiksni podniz (zastava -F у grep).
  3. Ostavite redove koji odgovaraju regularnom izrazu (flag -E у grep).
  4. Prema zadanim postavkama, pregled je obrnutim hronološkim redoslijedom, budući da su najnoviji zapisi obično prvi od interesa.
  5. Prikaži kontekst pored svake linije (opcije -A, -B и -C у grep, štampanje N redova prije, poslije i oko svake odgovarajuće linije, respektivno).
  6. Pregledajte dolazne zapise u realnom vremenu, sa ili bez filtriranja (u suštini tail -f | grep).
  7. Interfejs mora biti kompatibilan sa less, head, tail i drugi - prema zadanim postavkama, rezultati se trebaju vratiti bez ograničenja njihovog broja; linije se štampaju kao stream sve dok je korisnik zainteresovan da ih primi; signal SIGPIPE trebali bi tiho prekinuti prijenos dnevnika, baš kao što to rade tail, grep i drugi UNIX uslužni programi.

Реализация

Pretpostavljam da već nekako znate kako isporučiti dnevnike u ClickHouse. Ako ne, preporučujem da probate LSD и mačićaI ovaj članak o isporuci dnevnika.

Prvo morate odlučiti o osnovnoj shemi. Budući da obično želite da primate zapise sortirane po vremenu, čini se logičnim da ih tako pohranite. Ako postoji mnogo kategorija dnevnika i sve su istog tipa, onda možete napraviti kategoriju dnevnika kao prvu kolonu primarnog ključa - to će vam omogućiti da imate jednu tablicu umjesto nekoliko, što će biti veliki plus kada umetanje u ClickHouse (na serverima sa tvrdim diskovima, preporučuje se umetanje podataka najviše ~1 puta u sekundi za ceo server).

Odnosno, potrebna nam je otprilike sljedeća shema 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 nijedan otvoreni izvor sa realističnim zapisnicima koje bih mogao zgrabiti i preuzeti, pa sam uzeo ovo kao primjer recenzije proizvoda iz Amazona prije 2015. Naravno, njihova struktura nije potpuno ista kao kod tekstualnih dnevnika, ali za ilustraciju to nije važno.

upute za postavljanje Amazon recenzija na ClickHouse

Kreirajmo tabelu:

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 Amazon skupu podataka postoji samo datum za pregled, ali ne postoji tačno vrijeme, pa popunimo ove podatke nasumično.

Ne morate preuzimati sve tsv datoteke i ograničiti se na prvih ~10-20 da biste dobili prilično veliki skup podataka koji neće stati u 16 GB RAM-a. Za upload TSV fajlova 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 standardnom trajnom disku (koji je HDD) u Google Cloud-u veličine 1000 GB (uzeo sam ovu veličinu uglavnom da bi brzina bila malo veća, iako bi možda SSD tražene veličine bio jeftiniji) upload brzina je bila približno ~75 MB/sec na 4 jezgre.

  • Moram da rezervišem da radim u Google-u, ali koristio sam lični nalog i ovaj članak nema nikakve veze sa mojim radom u kompaniji

Napravit ću sve ilustracije sa ovim skupom podataka, jer 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 i možda neće dati nikakve rezultate dugo vremena ako se pronađe nekoliko podudaranja, preporučljivo je da možete prikazati napredak upita dok se ne prime prvi redovi sa rezultatom. Da biste to učinili, postoji parametar u HTTP sučelju koji vam omogućava da pošaljete napredak u HTTP zaglavljima: send_progress_in_http_headers=1. Nažalost, standardna Go biblioteka ne može čitati zaglavlja kako su primljena, ali ClickHouse podržava HTTP 1.0 sučelje (ne treba ga brkati sa 1.1!), tako da možete otvoriti sirovu TCP vezu na ClickHouse i poslati je tamo GET /?query=... HTTP/1.0nn i primamo zaglavlja i tijelo odgovora bez ikakvog izbjegavanja ili enkripcije, tako da u ovom slučaju ne moramo ni koristiti standardnu ​​biblioteku.

Streaming dnevnika iz ClickHouse

ClickHouse ima optimizaciju za upite sa ORDER BY relativno dugo (od 2019.?), pa je upit poput

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

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

Također, bilo bi vrlo zgodno kada bi ClickHouse sam poništio zahtjev kada je veza s njim zatvorena, ali to nije standardno ponašanje. Automatsko otkazivanje zahtjeva može se omogućiti korištenjem opcije cancel_http_readonly_queries_on_client_close=1.

Ispravno rukovanje SIGPIPE-om u Go

Kada izvršite, recimo, naredbu some_cmd | head -n 10, tačno kako je komanda some_cmd zaustavlja izvršenje kada head oduzeo 10 redova? Odgovor je jednostavan: kada head završava, cev se zatvara, a stdout komande some_cmd počinje da pokazuje, uslovno, "nigde". Kada some_cmd pokušava pisati u zatvorenu cijev, prima SIGPIPE signal, koji po defaultu tiho prekida program.

U Go ovo se takođe dešava podrazumevano, ali SIGPIPE signal handler takođe ispisuje "signal: SIGPIPE" ili sličnu poruku na kraju, a da obrišemo ovu poruku, samo treba da rukujemo SIGPIPE-om na način na koji ž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 greška (na primjer, koji je zahtjev izazvao paniku, ili koji povezani problemi su bili vidljivi prije pada), i u grep Ovo se radi pomoću opcija -A, -B i -C, koje prikazuju određeni broj redova nakon, prije i oko poruke.

Nažalost, nisam našao jednostavan način da to isto uradim u ClickHouseu, pa se za prikaz konteksta šalje dodatni zahtjev poput ovog u svaki red rezultata (detalji zavise od sortiranja i da li je 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

Pošto se zahtjev šalje gotovo odmah nakon što ClickHouse vrati odgovarajući red, on završava u kešu i općenito se zahtjev izvršava prilično brzo i troši malo CPU-a (obično zahtjev traje oko ~6 ms na mojoj virtuelnoj mašini).

Prikaži nove poruke u realnom vremenu

Da bismo prikazali dolazne poruke u (skoro) realnom vremenu, jednostavno izvršavamo zahtjev svakih nekoliko sekundi, pamteći posljednju vremensku oznaku na koju smo ranije naišli.

Primjeri naredbi

Kako tipične logscli komande izgledaju u praksi?

Ako ste preuzeli Amazonov skup podataka 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

Uslužni kod (bez dokumentacije) dostupan je na githubu na adresi https://github.com/YuriyNasretdinov/logscli. Bilo bi mi drago da čujem vaše mišljenje o mojoj ideji za interfejs konzole za pregledavanje dnevnika zasnovanog na ClickHouse-u.

izvor: www.habr.com

Dodajte komentar