Hvis du noen gang har brukt nettgrensesnitt for å se logger, har du sikkert lagt merke til hvordan disse grensesnittene som regel er tungvinte og (ofte) lite praktiske og responsive. Noen kan du bli vant til, noen er helt forferdelige, men det ser ut til at årsaken til alle problemene er at vi nærmer oss oppgaven med å se logger feil: vi prøver å lage et nettgrensesnitt der CLI (kommandolinjegrensesnitt) fungerer bedre. Jeg personlig er veldig komfortabel med å jobbe med tail, grep, awk og andre, og derfor for meg ville det ideelle grensesnittet for å jobbe med logger være noe som ligner på tail og grep, men som også kan brukes til å lese logger som kom fra mange servere. Det er, selvfølgelig, les dem fra ClickHouse!
*i henhold til habra-brukerens personlige mening
Møt logscli
Jeg kom ikke opp med et navn for grensesnittet mitt, og for å være ærlig, eksisterer det heller i form av en prototype, men hvis du umiddelbart vil se kildekoden, er du velkommen: (350 linjer med valgt Go-kode).
Evner
Målet mitt var å lage et grensesnitt som ville virke kjent for de som er vant til tail/grep, det vil si å støtte følgende ting:
- Se alle logger uten filtrering.
- La linjer inneholde en fast delstreng (flagg
-Fуgrep). - La linjer som samsvarer med det regulære uttrykket (flag
-Eуgrep). - Som standard er visningen i omvendt kronologisk rekkefølge, siden de nyeste loggene vanligvis er av interesse først.
- Vis kontekst ved siden av hver linje (alternativer
-A,-Bи-Cуgrep, utskrift av henholdsvis N linjer før, etter og rundt hver samsvarende linje). - Se innkommende logger i sanntid, med eller uten filtrering (i hovedsak
tail -f | grep). - Grensesnittet må være kompatibelt med
less,head,tailog andre - som standard skal resultater returneres uten begrensninger på antallet; linjer skrives ut som en strøm så lenge brukeren er interessert i å motta dem; signalSIGPIPEbør avbryte loggstrømming i det stille, akkurat som de gjørtail,grepog andre UNIX-verktøy.
implementering
Jeg vil anta at du allerede på en eller annen måte vet hvordan du leverer logger til ClickHouse. Hvis ikke, anbefaler jeg å prøve det и Og .
Først må du bestemme deg for basisordningen. Siden du vanligvis ønsker å motta logger sortert etter tid, virker det logisk å lagre dem på den måten. Hvis det er mange loggkategorier og alle er av samme type, kan du lage en loggkategori som den første kolonnen i primærnøkkelen - dette vil tillate deg å ha én tabell i stedet for flere, noe som vil være et stort pluss når innsetting i ClickHouse (på servere med harddisker anbefales det å sette inn data ikke mer enn ~1 ganger per sekund for hele serveren).
Det vil si at vi trenger omtrent følgende tabellskjema:
CREATE TABLE logs(
category LowCardinality(String), -- категория логов (опционально)
time DateTime, -- время события
millis UInt16, -- миллисекунды (могут быть и микросекунды, и т.д.): рекомендуется хранить, если событий много, чтобы было легче различать события между собой
..., -- ваши собственные поля, например имя сервера, уровень логирования, и так далее
message String -- текст сообщения
) ENGINE=MergeTree()
ORDER BY (category, time, millis)
Dessverre kunne jeg ikke umiddelbart finne noen åpne kilder med realistiske logger som jeg kunne hente og laste ned, så jeg tok dette i stedet som et eksempel . Naturligvis er strukturen deres ikke helt den samme som for tekstlogger, men for illustrasjonsformål er dette ikke viktig.
instruksjoner for opplasting av Amazon-anmeldelser til ClickHouse
La oss lage en tabell:
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
I Amazon-datasettet er det bare en dato for en anmeldelse, men det er ingen nøyaktig tid, så la oss fylle ut disse dataene med en randon.
Du trenger ikke å laste ned alle tsv-filene og begrense deg til de første ~10-20 for å få et ganske stort sett med data som ikke passer inn i 16 GB RAM. For å laste opp TSV-filer brukte jeg følgende kommando:
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
På en standard Persistent Disk (som er en HDD) i Google Cloud med en størrelse på 1000 GB (jeg tok denne størrelsen hovedsakelig slik at hastigheten var litt høyere, selv om kanskje en SSD i ønsket størrelse ville vært billigere) hastigheten var omtrent ~ 75 MB/sek på 4 kjerner.
- Jeg må reservere at jeg jobber hos Google, men jeg brukte en personlig konto og denne artikkelen har ingenting å gjøre med arbeidet mitt i selskapet
Jeg vil produsere alle illustrasjoner med dette spesielle datasettet, siden dette er alt jeg hadde for hånden.
Vis fremdriften for dataskanning
Siden vi i ClickHouse vil bruke en full skanning på en tabell med logger, og denne operasjonen kan ta betydelig tid og kanskje ikke gi noen resultater på lang tid hvis få treff blir funnet, er det tilrådelig å kunne vise fremdriften av spørringen til de første radene med resultatet er mottatt. For å gjøre dette er det en parameter i HTTP-grensesnittet som lar deg sende fremdrift i HTTP-hoder: send_progress_in_http_headers=1. Dessverre kan ikke standard Go-biblioteket lese overskrifter etter hvert som de mottas, men HTTP 1.0-grensesnittet (må ikke forveksles med 1.1!) støttes av ClickHouse, så du kan åpne en rå TCP-tilkobling til ClickHouse og sende den dit GET /?query=... HTTP/1.0nn og motta svarhodene og brødteksten uten escape eller kryptering, så i dette tilfellet trenger vi ikke engang å bruke standardbiblioteket.
Streaming av logger fra ClickHouse
ClickHouse har hatt optimalisering for spørringer med ORDER BY i relativt lang tid (siden 2019?), så en spørring som
SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESC
Den vil umiddelbart begynne å returnere linjer som har delstrengen "noe" i meldingen, uten å vente på at skanningen skal fullføres.
Dessuten ville det være veldig praktisk hvis ClickHouse selv kansellerte forespørselen når tilkoblingen til den ble lukket, men dette er ikke standardoppførselen. Automatisk kansellering av forespørsel kan aktiveres ved å bruke alternativet cancel_http_readonly_queries_on_client_close=1.
Riktig håndtering av SIGPIPE in Go
Når du utfører, si, kommandoen some_cmd | head -n 10, nøyaktig hvordan kommandoen some_cmd stopper utførelse når head trukket 10 linjer? Svaret er enkelt: når head slutter, røret lukkes, og stdouten til some_cmd-kommandoen begynner å peke, betinget, "til ingensteds". Når some_cmd prøver å skrive til et lukket rør, .
I Go skjer dette også som standard, men SIGPIPE-signalbehandleren skriver også "signal: SIGPIPE" eller en lignende melding på slutten, og for å fjerne denne meldingen trenger vi bare å håndtere SIGPIPE selv slik vi vil, det vil si bare stille exit:
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGPIPE)
go func() {
<-ch
os.Exit(0)
}()
Vis meldingskontekst
Ofte vil du se konteksten som en feil oppstod i (for eksempel hvilken forespørsel som forårsaket panikk, eller hvilke relaterte problemer som var synlige før krasjen), og i grep Dette gjøres ved å bruke alternativene -A, -B og -C, som viser det angitte antall linjer henholdsvis etter, før og rundt meldingen.
Dessverre har jeg ikke funnet en enkel måte å gjøre det samme i ClickHouse, så for å vise konteksten sendes en ekstra forespørsel som denne til hver linje i resultatet (detaljene avhenger av sorteringen og om konteksten vises før eller etter):
SELECT time,millis,review_body FROM amazon
WHERE (time = 'ВРЕМЯ_СОБЫТИЯ' AND millis < МИЛЛИСЕКУНДЫ_СОБЫТИЯ) OR (time < 'ВРЕМЯ_СОБЫТИЯ')
ORDER BY time DESC, millis DESC
LIMIT КОЛИЧЕСТВО_СТРОК_КОНТЕКСТА
SETTINGS max_threads=1
Siden forespørselen sendes nesten umiddelbart etter at ClickHouse returnerer den tilsvarende linjen, havner den i cachen og generelt blir forespørselen utført ganske raskt og bruker litt CPU (vanligvis tar forespørselen ca. ~6 ms på min virtuelle maskin).
Vis nye meldinger i sanntid
For å vise innkommende meldinger i (nesten) sanntid, utfører vi ganske enkelt forespørselen med noen sekunders mellomrom, og husker det siste tidsstemplet vi møtte før.
Eksempler på kommandoer
Hvordan ser typiske logscli-kommandoer ut i praksis?
Hvis du lastet ned Amazon-datasettet som jeg nevnte i begynnelsen av artikkelen, kan du kjøre følgende kommandoer:
# Показать строки, где встречается слово 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
referanser
Verktøykoden (uten dokumentasjon) er tilgjengelig på github på . Jeg vil gjerne høre dine tanker om ideen min om et konsollgrensesnitt for visning av logger basert på ClickHouse.
Kilde: www.habr.com
