Nëse keni pasur ndonjëherë rastin të përdorni ndërfaqet në internet për të parë logët, atëherë me siguri keni vënë re se sa shpesh këto ndërfaqe janë të ngarkuara dhe (shpesh) jo shumë të këndshme dhe reaguese. Disa mund të përdoren, disa janë krejt të tmerrshme, por, siç mendoj, shkaku i të gjitha problemeve qëndron në faktin se ne po e qasemi gabim problemin e shikimit të logëve: po përpiqemi të krijojmë një ndërfaqe në internet aty ku funksionon më mirë CLI (ndërfaqja e komandës). Personal, ndihem shumë rehat duke punuar me tail, grep, awk dhe të tjera, dhe prandaj ndërfaqja ideale për mua për të punuar me logët do të ishte diçka e ngjashme me tail dhe grep, por që mund të përdorej për të lexuar logët që vinë nga shumë servera. Pra, sigurisht, t'i lexosh ato nga ClickHouse!
*sipërmarrje personale e një përdoruesi të Habrahabr
Përshendete logscli
Nuk e kam menduar ndonjë emër për ndërfaqen time, dhe, për të qenë i sinqertë, ajo ekziston më tepër si një prototip, por nëse dëshironi të shihni menjëherë kodet burimore, atëherë mirë se vini: (350 rreshta kode të zgjedhura në Go).
Mundësitë
Kisha si qëllim të krijoja një ndërfaqe që do të dukej e njohur për ata që janë mësuar me tail/grep, pra të mbështesë gjërat e mëposhtme:
- Shikimi i të gjithë logëve, pa filtrimin.
- Lërni rreshtat, në të cilat përmban një nënstring të caktuar (flag
-Figrep). - Lërni rreshtat, që përputhen me shprehjen e rregullt (flag
-Eigrep). - Nga e para, shikimi në rendin e kundërt kronologjik, ndoshta sepse përgjithësisht, logët më të freskët janë më interesantë.
- Të shfaqë kontekstin afër çdo rreshti (parametrat
-A,-Bdhe-Cigrep, që printon N rreshta përpara, pas, dhe përreth çdo rreshti përputhës përkatës). - Shikimi i logëve që vijnë në kohë reale, me filtrimin dhe pa (për esencën
tail -f | grep). - Ndërfaqja duhet të jetë kompatibile me
less,head,taildhe të tjera - si parazgjedhje, rezultatet duhet të kthehen pa kufizime në numrin e tyre; rreshtat printohen në mënyrë të vazhdueshme deri sa përdoruesi të jetë i interesuar për t’i marrë; sinjaliSIGPIPEduhet të ndërpresë në heshtje streaming-un e logëve, ashtu siç bëjnëtail,grepdhe utilitarët e tjerë të UNIX-it.
Implementimi
Do të supozoj se tashmë dini ndonjë mënyrë për të dërguar logët në ClickHouse. Nëse jo, atëherë rekomandoj të provoni dhe , si dhe .
Së pari, duhet të vendosim për skemën e bazës së të dhënave. Duke qenë se zakonisht dëshirojmë të marrim logët të renditura sipas kohës, duket logjik t'i ruajmë ashtu. Nëse ka shumë kategori logësh dhe të gjitha janë njëjta, atëherë mund të bëjmë kategorinë e logëve kolonën e parë të kyçit primar - kjo do të lejojë të kemi një tabelë në vend të disa, që do të ishte një avantajh i madh në momentin e futjes në ClickHouse (në serverët me disqe të ngurta rekomandohet të fusni të dhënat jo më shpesh se ~1 herë në sekondë) për të gjithë serverin).
Pra, na nevojitet një skemë tabelash e tillë:
CREATE TABLE logs(
category LowCardinality(String), -- kategoria e logëve (opsionale)
time DateTime, -- koha e ngjarjes
millis UInt16, -- milisekondat (mund të jenë edhe mikroskedonda, etj.): rekomandohet të ruhet, nëse ka shumë ngjarje, për ta bërë më të lehtë dallimin mes tyre
..., -- fushat tuaja të veta, si emri i serverit, niveli i logimit, etj.
message String -- tekstin e mesazhit
) ENGINE=MergeTree()
ORDER BY (category, time, millis)Fatkeqësisht, nuk arrita menjëherë të gjej ndonjë burim të hapur me logë realiste që mund të merret dhe shkarkohet, prandaj mora për shembull . Sigurisht, struktura e tyre nuk është krejtësisht e njëjtë me atë të logëve tekstualë, por për ilustrim kjo nuk është thelbësore.
udhiçues për ngarkimin e komenteve të Amazon në ClickHouse
Le të krijojmë tabelën:
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=8192Në setin e të dhënave të Amazonit ka vetëm datën për rishikimin, por nuk ka një kohë të saktë, prandaj do ta mbushim këtë të dhënë me rastësi.
Nuk është e nevojshme të shkarkoni të gjithë skedarët tsv dhe mund të kufizoheni në 10-20 të parët, për të marrë tashmë një grup të dhënash mjaft të madh, që nuk do të hyjë në 16 GB RAM. Për ngarkimin e skedarëve TSV përdora komandën e mëposhtme:
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'
doneNë një Disk të Privuar Standard (i cili është HDD) në Google Cloud me madhësi prej 1000 Gb (këtu e kam marrë këtë madhësi kryesisht për të pasur pak më shumë shpejtësi, ndonëse ndoshta një SSD me madhësinë e nevojshme do të dilte më lirë) shpejtësia e ngarkimit ishte rreth ~75 Mb/sec në 4 bërthama.
- duhet të përmend se punoj në Google, por kam përdorur një llogari personale dhe ky artikull nuk ka lidhje me punën time në kompaninë.
Të gjitha ilustraimet do t'i bëj me këtë dataset, sepse është gjithçka që kishim në dorë.
Shfaqja e përparimit të skanimit të të dhënave
Duke qenë se në ClickHouse do të përdorim skanimin e plotë mbi tabelën me logjet, dhe kjo operacion mund të marrë një kohë të konsiderueshme dhe të mos japë asnjë rezultat për një kohë të gjatë nëse gjenden pak përputhje, është e dëshirueshme të jemi në gjendje të tregojmë përparimin e ekzekutimit të kërkesës deri sa të marrim rreshtat e parë me rezultat. Për këtë, në ndërfaqen HTTP ka një parametër që lejon të jepet përparimi në titujt HTTP: send_progress_in_http_headers=1. Fatkeqësisht, biblioteka standarde Go nuk arrin të lexojë titujt teksa i merr, por ndërfaqja HTTP 1.0 (mos e ngatërroni me 1.1!) mbështetet nga ClickHouse, prandaj mund të hapim një lidhje TCP të papërpunuar me ClickHouse, të dërgojmë aty GET \/?query=... HTTP\/1.0nn dhe të marrim si përgjigje titujt dhe trupin e përgjigjes pa asnjë formë ekranuese dhe enkriptimi, kështu që për këtë rast nuk na nevojitet as biblioteka standarde.
Streamimi i logjeve nga ClickHouse
Që nga viti 2019, ClickHouse ka optimizuar kërkesat me ORDER BY, kështu që një kërkesë e tillë
SELECT time, millis, message
FROM logs
WHERE message LIKE '%diçka%'
ORDER BY time DESC, millis DESCdo të fillojë të kthejë menjëherë rreshtat që kanë një nënstringë "diçka" në mesazh, pa pritur që skanimi të përfundojë.
Gjithashtu, do të ishte shumë e dobishme nëse ClickHouse do të anulonte vetë kërkesën kur lidhja mbyllet, por kjo nuk është sjellja e paracaktuar. Anulimi automatik i kërkesës mund të aktivizohet me opsionin cancel_http_readonly_queries_on_client_close=1.
Trajtimi i saktë i SIGPIPE në Go
Kur ju ekzekutoni, le të themi, komandën some_cmd | head -n 10, si ndodh që komandë some_cmd ndalon ekzekutimin e saj, kur head lexon 10 rreshta? Pjesa e përgjigjes është e thjeshtë: kur head përfundon, pipe mbyllet, dhe stdout i komandës some_cmd fillon të tregojë, në mënyrë kushtore, «askund». Kur some_cmd përpiqet të shkruajë në pipe të mbyllur, .
Në Go, kjo ndodh gjithashtu siç është parazgjedhur, por manipuluesi i sinjalit SIGPIPE gjithashtu printon në fund "sinjali: SIGPIPE" ose një mesazh të ngjashëm, dhe për ta hequr këtë mesazh, thjesht duhet ta αντιμετώπισης SIGPIPE ashtu siç e duam, domethënë të dalim në heshtje.
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGPIPE)
go func() {
<-ch
os.Exit(0)
}()Shfaq kontekstin e mesazhit
Ndonjëherë dëshirojmë të shohim kontekstin në të cilin ndodhi ndonjë gabim (p.sh., cili kërkesë shkaktoi panik, ose cilat probleme të tjera ishin të dukshme para rënies), dhe në grep për këtë shërbejnë opsionet -A, -B dhe -C, të cilat tregojnë numrin e caktuar të rreshtave pas, para, dhe rreth mesazhit përkatës.
Fatkeqësisht, nuk kam gjetur një mënyrë të thjeshtë për të bërë të njëjtën gjë në ClickHouse, prandaj, për të shfaqur kontekstin, një kërkesë shtesë dërgohet për çdo rresht rezultati në një format të ngjashëm (detajet varen nga renditja dhe nëse shfaqet konteksti para ose pas):
SELECT time,millis,review_body FROM amazon
WHERE (time = 'KOHA_E_DOKUMENTIT' AND millis < MILLISEKUNDAT_E_DOKUMENTIT) OR (time < 'KOHA_E_DOKUMENTIT')
ORDER BY time DESC, millis DESC
LIMIT NUMRI_I_RRESHTAVE_TË_KONTEXTIT
SETTINGS max_threads=1Pasi kërkesa dërgohet pothuajse menjëherë pas asaj që ClickHouse ktheu rreshtin përkatës, ajo hyn në memorie dhe përgjithësisht kërkesa ekzekutohet mjaft shpejt dhe harxhon pak CPU (zakonisht kërkesa zgjat rreth ~6 ms në makinen time virtuale).
Shfaqja e mesazheve të reja në kohë reale
Për të treguar mesazhet që vijnë në mënyrë (gati) reale, thjesht ekzekutojmë kërkesën çdo disa sekonda, duke mbajtur mend timestampin e fundit që kemi takuar më parë.
Shembuj komandash
Si duken komandat tipike logscli në praktikë?
Nëse keni ngarkuar datasetin Amazon që përmenda në fillim të artikullit, atëherë mund të ekzekutoni komandat në vijim:
# Показать строки, где встречается слово 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' -tailfLinket
Kodi i utilitarit (pa dokumentacion) është në dispozicion në github në adresën . Do jem i lumtur të dëgjoj mendimet tuaja në lidhje me idenë time për një ndërfaqe konsoli për shikimin e logeve të bazuara në ClickHouse.
Burimi: habr.com
