Vi utvecklar det mest bekväma gränssnittet i världen* för att visa loggar

Vi utvecklar det mest bekväma gränssnittet i världen* för att visa loggar Om du någon gång har använt webbgränssnitt för att se loggar, så har du förmodligen märkt hur dessa gränssnitt som regel är krångliga och (ofta) inte särskilt bekväma och lyhörda. Vissa kan man vänja sig vid, vissa är helt fruktansvärda, men det verkar för mig som att orsaken till alla problem är att vi närmar oss uppgiften att visa loggar felaktigt: vi försöker skapa ett webbgränssnitt där CLI (command line interface) fungerar bättre. Jag personligen är väldigt bekväm med att arbeta med tail, grep, awk och andra, och därför för mig skulle det perfekta gränssnittet för att arbeta med loggar vara något som liknar tail och grep, men som också kan användas för att läsa loggar som kom från många servrar. Det är, naturligtvis, läs dem från ClickHouse!

*enligt habra-användarens personliga åsikt du äger

Möt logscli

Jag kom inte på något namn för mitt gränssnitt, och, för att vara ärlig, existerar det snarare i form av en prototyp, men om du omedelbart vill se källkoden, är du välkommen: https://github.com/YuriyNasretdinov/logscli (350 rader med vald Go-kod).

Capabilities

Mitt mål var att skapa ett gränssnitt som skulle verka bekant för dem som är vana vid tail/grep, det vill säga att stödja följande saker:

  1. Visa alla loggar, utan filtrering.
  2. Lämna rader som innehåller en fast delsträng (flagga -F у grep).
  3. Lämna linjer som matchar det reguljära uttrycket (flagga -E у grep).
  4. Som standard är visningen i omvänd kronologisk ordning, eftersom de senaste loggarna vanligtvis är av intresse först.
  5. Visa sammanhang bredvid varje rad (alternativ -A, -B и -C у grep, utskrift av N rader före, efter respektive runt varje matchande linje).
  6. Visa inkommande loggar i realtid, med eller utan filtrering (i huvudsak tail -f | grep).
  7. Gränssnittet måste vara kompatibelt med less, head, tail och andra - som standard ska resultat returneras utan begränsningar för deras antal; rader skrivs ut som en ström så länge som användaren är intresserad av att ta emot dem; signal SIGPIPE bör tyst avbryta loggströmning, precis som de gör tail, grep och andra UNIX-verktyg.

genomförande

Jag antar att du redan på något sätt vet hur man levererar loggar till ClickHouse. Om inte, rekommenderar jag att du provar det lsd и kattungehusOch den här artikeln om stockleverans.

Först måste du bestämma dig för basschemat. Eftersom du vanligtvis vill ta emot loggar sorterade efter tid, verkar det logiskt att lagra dem på det sättet. Om det finns många loggkategorier och alla är av samma typ, kan du skapa en loggkategori som den första kolumnen i primärnyckeln - detta gör att du kan ha en tabell istället för flera, vilket kommer att vara ett stort plus när infogning i ClickHouse (på servrar med hårddiskar rekommenderas att infoga data inte mer än ~1 gånger per sekund för hela servern).

Det vill säga, vi behöver ungefär följande tabellschema:

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

Tyvärr kunde jag inte direkt hitta några öppna källor med realistiska loggar som jag kunde ta och ladda ner, så jag tog detta istället som ett exempel recensioner av produkter från Amazon fram till 2015. Naturligtvis är deras struktur inte exakt densamma som textloggar, men i illustrationssyfte är detta inte viktigt.

instruktioner för att ladda upp Amazon-recensioner till ClickHouse

Låt oss skapa 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 Amazons datauppsättning finns det bara ett datum för en recension, men det finns ingen exakt tid, så låt oss fylla i dessa data med en randon.

Du behöver inte ladda ner alla tsv-filer och begränsa dig till de första ~10-20 för att få en ganska stor uppsättning data som inte får plats i 16 GB RAM. För att ladda upp TSV-filer använde jag följande 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 är en hårddisk) i Google Cloud med en storlek på 1000 GB (jag tog den här storleken främst för att hastigheten skulle vara lite högre, även om kanske en SSD i den storlek som krävs hade varit billigare) uppladdningen hastigheten var ungefär ~ 75 MB/sek på 4 kärnor.

  • Jag måste göra en reservation för att jag jobbar på Google, men jag använde ett personligt konto och den här artikeln har ingenting att göra med mitt arbete på företaget

Jag kommer att producera alla illustrationer med just denna datauppsättning, eftersom detta är allt jag hade till hands.

Visa framsteg för dataskanning

Eftersom vi i ClickHouse kommer att använda en fullständig skanning på en tabell med loggar, och denna operation kan ta en betydande tid och kanske inte ger några resultat under en lång tid om få matchningar hittas, är det tillrådligt att kunna visa förloppet för frågan tills de första raderna med resultatet tas emot. För att göra detta finns det en parameter i HTTP-gränssnittet som låter dig skicka framsteg i HTTP-rubriker: send_progress_in_http_headers=1. Tyvärr kan standard Go-biblioteket inte läsa rubriker när de tas emot, men HTTP 1.0-gränssnittet (inte att förväxla med 1.1!) stöds av ClickHouse, så du kan öppna en rå TCP-anslutning till ClickHouse och skicka den dit GET /?query=... HTTP/1.0nn och ta emot svarsrubriker och text utan någon flykt eller kryptering, så i det här fallet behöver vi inte ens använda standardbiblioteket.

Strömmande loggar från ClickHouse

ClickHouse har haft optimering för frågor med ORDER BY under en relativt lång tid (sedan 2019?), så en fråga som t.ex.

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

Den kommer omedelbart att börja returnera rader som har delsträngen "något" i sitt meddelande, utan att vänta på att skanningen ska avslutas.

Det skulle också vara mycket bekvämt om ClickHouse själv avbröt begäran när anslutningen till den stängdes, men detta är inte standardbeteendet. Automatisk annullering av begäran kan aktiveras med alternativet cancel_http_readonly_queries_on_client_close=1.

Korrekt hantering av SIGPIPE in Go

När du utför, säg, kommandot some_cmd | head -n 10, exakt hur kommandot some_cmd stoppar exekvering när head subtraherad 10 rader? Svaret är enkelt: när head slutar, röret stängs och stdout för kommandot some_cmd börjar peka, villkorligt, "till ingenstans". När some_cmd försöker skriva till ett stängt rör, den tar emot en SIGPIPE-signal, som tyst avslutar programmet som standard.

I Go händer detta också som standard, men SIGPIPE-signalhanteraren skriver också ut "signal: SIGPIPE" eller ett liknande meddelande i slutet, och för att rensa detta meddelande behöver vi bara hantera SIGPIPE själva som vi vill, det vill säga bara tyst utgång:

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

Visa meddelandekontext

Ofta vill du se i vilket sammanhang ett fel inträffade (till exempel vilken begäran som orsakade panik eller vilka relaterade problem som var synliga före kraschen), och i grep Detta görs med alternativen -A, -B och -C, som visar det angivna antalet rader efter, före och runt meddelandet.

Tyvärr har jag inte hittat något enkelt sätt att göra detsamma i ClickHouse, så för att visa sammanhanget skickas en ytterligare begäran som denna till varje rad i resultatet (detaljerna beror på sorteringen och om sammanhanget visas innan 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

Eftersom förfrågan skickas nästan direkt efter att ClickHouse returnerar motsvarande rad hamnar den i cachen och i allmänhet exekveras förfrågan ganska snabbt och förbrukar lite CPU (vanligtvis tar förfrågan ca ~6 ms på min virtuella maskin).

Visa nya meddelanden i realtid

För att visa inkommande meddelanden i (nästan) realtid, kör vi helt enkelt begäran en gång med några sekunders mellanrum och kommer ihåg den senaste tidsstämpeln som vi stötte på tidigare.

Kommandoexempel

Hur ser typiska logscli-kommandon ut i praktiken?

Om du laddade ner Amazon-datasetet som jag nämnde i början av artikeln kan du köra följande kommandon:

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

referenser

Verktygskoden (utan dokumentation) är tillgänglig på github på https://github.com/YuriyNasretdinov/logscli. Jag skulle vara glad att höra dina tankar om min idé om ett konsolgränssnitt för att visa loggar baserade på ClickHouse.

Källa: will.com

Lägg en kommentar