Vi er ved at udvikle den mest bekvemme grænseflade i verden* til visning af logfiler

Vi er ved at udvikle den mest bekvemme grænseflade i verden* til visning af logfiler Hvis du nogensinde har brugt webgrænseflader til at se logfiler, så har du sikkert lagt mærke til, hvordan disse grænseflader som regel er besværlige og (ofte) ikke særlig bekvemme og responsive. Nogle kan du vænne dig til, nogle er helt forfærdelige, men det forekommer mig, at årsagen til alle problemerne er, at vi griber opgaven med at se logfiler forkert: vi forsøger at lave en webgrænseflade, hvor CLI (kommandolinjegrænseflade) fungerer bedre. Jeg er personligt meget tryg ved at arbejde med tail, grep, awk og andre, og derfor ville den ideelle grænseflade til at arbejde med logs for mig være noget, der ligner tail og grep, men som også kunne bruges til at læse logs, der kom fra mange servere. Det er selvfølgelig, læs dem fra ClickHouse!

*ifølge habra-brugerens personlige mening du styrer

Mød logscli

Jeg fandt ikke på et navn til min grænseflade, og for at være ærlig eksisterer den snarere i form af en prototype, men hvis du straks vil se kildekoden, så er du velkommen: https://github.com/YuriyNasretdinov/logscli (350 linjer med valgt Go-kode).

Capabilities

Mit mål var at lave en grænseflade, der ville virke bekendt for dem, der er vant til tail/grep, det vil sige at understøtte følgende ting:

  1. Se alle logfiler uden filtrering.
  2. Lad linjer indeholde en fast understreng (flag -F у grep).
  3. Efterlad linjer, der matcher det regulære udtryk (flag -E у grep).
  4. Som standard er visningen i omvendt kronologisk rækkefølge, da de seneste logfiler normalt er af interesse først.
  5. Vis kontekst ud for hver linje (valgmuligheder -A, -B и -C у grep, udskrivning af N linjer henholdsvis før, efter og omkring hver matchende linje).
  6. Se indgående logfiler i realtid, med eller uden filtrering (i det væsentlige tail -f | grep).
  7. Grænsefladen skal være kompatibel med less, head, tail og andre - som standard skal resultater returneres uden begrænsninger på deres antal; linjer udskrives som en strøm, så længe brugeren er interesseret i at modtage dem; signal SIGPIPE bør lydløst afbryde logstreaming, ligesom de gør tail, grep og andre UNIX-værktøjer.

implementering

Jeg vil antage, at du allerede på en eller anden måde ved, hvordan du leverer logfiler til ClickHouse. Hvis ikke, anbefaler jeg at prøve det lsd и killingehusog denne artikel om loglevering.

Først skal du beslutte dig for basisordningen. Da du normalt ønsker at modtage logs sorteret efter tid, virker det logisk at gemme dem på den måde. Hvis der er mange logkategorier, og de alle er af samme type, så kan du lave en logkategori som den første kolonne i primærnøglen - dette vil tillade dig at have én tabel i stedet for flere, hvilket vil være et stort plus, når indsættelse i ClickHouse (på servere med harddiske anbefales det ikke at indsætte data mere end ~1 gange pr. sekund for hele serveren).

Det vil sige, at vi har brug for omtrent følgende tabelskema:

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

Desværre kunne jeg ikke umiddelbart finde nogen åbne kilder med realistiske logfiler, som jeg kunne få fat i og downloade, så jeg tog dette i stedet som et eksempel anmeldelser af produkter fra Amazon før 2015. Naturligvis er deres struktur ikke helt den samme som tekstlogfiler, men til illustrationsformål er dette ikke vigtigt.

instruktioner til upload af Amazon-anmeldelser til ClickHouse

Lad os lave en tabel:

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-datasættet er der kun en dato for en anmeldelse, men der er ingen nøjagtig tid, så lad os udfylde disse data med en randon.

Du behøver ikke at downloade alle tsv-filerne og begrænse dig til de første ~10-20 for at få et ret stort sæt data, der ikke passer ind i 16 GB RAM. For at uploade TSV-filer brugte 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 tog hovedsageligt denne størrelse, så hastigheden var lidt højere, selvom måske en SSD i den krævede størrelse ville have været billigere) blev uploadet hastigheden var ca. ~ 75 MB/sek. på 4 kerner.

  • Jeg skal reservere, at jeg arbejder hos Google, men jeg brugte en personlig konto, og denne artikel har intet at gøre med mit arbejde i virksomheden

Jeg vil producere alle illustrationer med dette særlige datasæt, da det er alt, jeg havde ved hånden.

Vis datascanningsfremskridt

Da vi i ClickHouse vil bruge en fuld scanning på en tabel med logfiler, og denne operation kan tage en betydelig mængde tid og muligvis ikke giver resultater i lang tid, hvis der findes få matches, er det tilrådeligt at være i stand til at vise forløbet af forespørgslen, indtil de første rækker med resultatet modtages. For at gøre dette er der en parameter i HTTP-grænsefladen, der giver dig mulighed for at sende fremskridt i HTTP-headere: send_progress_in_http_headers=1. Desværre kan standard Go-biblioteket ikke læse headere, efterhånden som de modtages, men HTTP 1.0-grænsefladen (ikke at forveksle med 1.1!) understøttes af ClickHouse, så du kan åbne en rå TCP-forbindelse til ClickHouse og sende den dertil GET /?query=... HTTP/1.0nn og modtag svarhovederne og brødteksten uden escape eller kryptering, så i dette tilfælde behøver vi ikke engang at bruge standardbiblioteket.

Streaming af logfiler fra ClickHouse

ClickHouse har haft optimering til forespørgsler med ORDER BY i relativt lang tid (siden 2019?), så en forespørgsel som f.eks.

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

Den vil straks begynde at returnere linjer, der har understrengen "noget" i deres besked, uden at vente på, at scanningen er færdig.

Det ville også være meget praktisk, hvis ClickHouse selv annullerede anmodningen, da forbindelsen til den blev lukket, men dette er ikke standardadfærden. Automatisk annullering af anmodninger kan aktiveres ved hjælp af muligheden cancel_http_readonly_queries_on_client_close=1.

Korrekt håndtering af SIGPIPE in Go

Når du udfører, for eksempel, kommandoen some_cmd | head -n 10, præcis hvordan kommandoen some_cmd stopper udførelse når head trukket 10 linjer fra? Svaret er enkelt: hvornår head slutter, røret lukkes, og stdout'en for some_cmd-kommandoen begynder at pege betinget "til ingen steder". Hvornår some_cmd forsøger at skrive til et lukket rør, den modtager et SIGPIPE-signal, som stille afslutter programmet som standard.

I Go sker dette også som standard, men SIGPIPE-signalhandleren udskriver også "signal: SIGPIPE" eller en lignende besked til sidst, og for at fjerne denne besked skal vi bare håndtere SIGPIPE selv, som vi vil, det vil sige bare lydløst Afslut:

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

Vis meddelelseskontekst

Ofte ønsker du at se den kontekst, hvori en fejl opstod (f.eks. hvilken anmodning, der forårsagede panik, eller hvilke relaterede problemer, der var synlige før nedbruddet), og i grep Dette gøres ved hjælp af -A, -B og -C mulighederne, som viser det angivne antal linjer henholdsvis efter, før og omkring beskeden.

Desværre har jeg ikke fundet en nem måde at gøre det samme i ClickHouse, så for at vise konteksten sendes en ekstra anmodning som denne til hver linje i resultatet (detaljerne afhænger af sorteringen og om konteksten vises før eller efter):

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

Da anmodningen sendes næsten umiddelbart efter, at ClickHouse har returneret den tilsvarende linje, ender den i cachen og generelt udføres anmodningen ret hurtigt og bruger lidt CPU (normalt tager anmodningen ca. ~6 ms på min virtuelle maskine).

Vis nye beskeder i realtid

For at vise indgående beskeder i (næsten) realtid, udfører vi blot anmodningen en gang med få sekunders mellemrum og husker det sidste tidsstempel, som vi stødte på før.

Eksempler på kommandoer

Hvordan ser typiske logscli-kommandoer ud i praksis?

Hvis du downloadede Amazon-datasættet, som jeg nævnte i begyndelsen af ​​artiklen, kan du kø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

RЎSЃS <P "RєRё

Hjælpekoden (uden dokumentation) er tilgængelig på github på https://github.com/YuriyNasretdinov/logscli. Jeg ville være glad for at høre dine tanker om min idé til en konsolgrænseflade til visning af logfiler baseret på ClickHouse.

Kilde: www.habr.com

Tilføj en kommentar