Ons ontwikkel die gerieflikste koppelvlak ter wêreld* om logs te bekyk

Ons ontwikkel die gerieflikste koppelvlak ter wêreld* om logs te bekyk As jy al ooit webkoppelvlakke gebruik het om logs te sien, dan het jy waarskynlik opgemerk hoe, as 'n reël, hierdie koppelvlakke omslagtig en (dikwels) nie baie gerieflik en reageer nie. Sommige kan jy gewoond raak, sommige is absoluut verskriklik, maar dit lyk vir my dat die rede vir al die probleme is dat ons die taak om logs verkeerd te bekyk benader: ons probeer om 'n webkoppelvlak te skep waar die CLI (command line interface) werk beter. Ek persoonlik is baie gemaklik om met tail, grep, awk en ander te werk, en daarom is die ideale koppelvlak vir my om met logs te werk iets soortgelyk aan tail en grep, maar wat ook gebruik kan word om logs te lees wat van baie bedieners af kom. Dit is natuurlik, lees hulle van ClickHouse!

*volgens die persoonlike mening van die habra-gebruiker jou ROCK

Ontmoet logscli

Ek het nie 'n naam vir my koppelvlak uitgedink nie, en, om eerlik te wees, bestaan ​​dit eerder in die vorm van 'n prototipe, maar as jy dadelik die bronkode wil sien, dan is jy welkom: https://github.com/YuriyNasretdinov/logscli (350 reëls van geselekteerde Go-kode).

Vermoëns

My doel was om 'n koppelvlak te maak wat bekend sal lyk vir diegene wat gewoond is aan tail/grep, dit wil sê om die volgende dinge te ondersteun:

  1. Bekyk alle logs, sonder om te filter.
  2. Laat lyne wat 'n vaste substring bevat (vlag -F у grep).
  3. Laat lyne wat ooreenstem met die gereelde uitdrukking (vlag -E у grep).
  4. By verstek is besigtiging in omgekeerde chronologiese volgorde, aangesien die mees onlangse logs gewoonlik eerste van belang is.
  5. Wys konteks langs elke reël (opsies -A, -B и -C у grep, druk onderskeidelik N reëls voor, na en rondom elke ooreenstemmende lyn).
  6. Bekyk inkomende logs in reële tyd, met of sonder filtering (in wese tail -f | grep).
  7. Die koppelvlak moet versoenbaar wees met less, head, tail en ander - by verstek moet resultate teruggestuur word sonder beperkings op hul aantal; reëls word as 'n stroom gedruk solank die gebruiker daarin belangstel om dit te ontvang; sein SIGPIPE moet die stroom van logboeke stilweg onderbreek, net soos hulle doen tail, grep en ander UNIX-nutsprogramme.

Implementering

Ek sal aanneem dat jy reeds op een of ander manier weet hoe om logs aan ClickHouse te lewer. Indien nie, beveel ek aan om dit te probeer lsd и katjiehuisieEn hierdie artikel oor logaflewering.

Eerstens moet jy besluit oor die basisskema. Aangesien jy gewoonlik logs wat volgens tyd gesorteer wil ontvang, lyk dit logies om dit so te stoor. As daar baie log kategorieë is en hulle is almal van dieselfde tipe, dan kan jy 'n log kategorie maak as die eerste kolom van die primêre sleutel - dit sal jou toelaat om een ​​tabel in plaas van verskeie te hê, wat 'n groot pluspunt sal wees wanneer invoeging in ClickHouse (op bedieners met hardeskywe word dit aanbeveel om data nie meer as ~1 keer per sekonde in te voeg nie vir die hele bediener).

Dit wil sê, ons benodig ongeveer die volgende tabelskema:

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

Ongelukkig kon ek nie dadelik enige oop bronne vind met realistiese logs wat ek kon gryp en aflaai nie, so ek het dit eerder as 'n voorbeeld geneem resensies van produkte van Amazon voor 2015. Natuurlik is hul struktuur nie presies dieselfde as dié van tekslogboeke nie, maar vir illustrasiedoeleindes is dit nie belangrik nie.

instruksies vir die oplaai van Amazon-resensies na ClickHouse

Kom ons skep 'n 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

In die Amazon-datastel is daar slegs 'n datum vir 'n hersiening, maar daar is geen presiese tyd nie, so kom ons vul hierdie data met 'n randon in.

Jy hoef nie al die tsv-lêers af te laai en jouself tot die eerste ~10-20 te beperk om 'n redelike groot stel data te kry wat nie in 16 GB RAM sal pas nie. Om TSV-lêers op te laai, het ek die volgende opdrag gebruik:

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

Op 'n standaard Persistent Disk (wat 'n HDD is) in Google Cloud met 'n grootte van 1000 GB (ek het hierdie grootte hoofsaaklik geneem sodat die spoed 'n bietjie hoër was, alhoewel miskien 'n SSD van die vereiste grootte goedkoper sou gewees het) die oplaai spoed was ongeveer ~ 75 MB/sek op 4 kerne.

  • Ek moet 'n bespreking maak dat ek by Google werk, maar ek het 'n persoonlike rekening gebruik en hierdie artikel het niks te doen met my werk by die maatskappy nie

Ek sal alle illustrasies met hierdie spesifieke datastel produseer, aangesien dit al is wat ek byderhand gehad het.

Wys dataskanderingsvordering

Aangesien ons in ClickHouse 'n volledige skandering op 'n tabel met logs sal gebruik, en hierdie bewerking 'n aansienlike hoeveelheid tyd kan neem en dalk vir 'n lang tyd geen resultate sal lewer as min passings gevind word nie, is dit raadsaam om die vordering van die navraag totdat die eerste rye met die resultaat ontvang is. Om dit te doen, is daar 'n parameter in die HTTP-koppelvlak waarmee u vordering in HTTP-opskrifte kan stuur: send_progress_in_http_headers=1. Ongelukkig kan die standaard Go-biblioteek nie kopskrifte lees soos dit ontvang word nie, maar die HTTP 1.0-koppelvlak (nie te verwar met 1.1 nie!) word deur ClickHouse ondersteun, so jy kan 'n rou TCP-verbinding na ClickHouse oopmaak en dit daarheen stuur GET /?query=... HTTP/1.0nn en ontvang die antwoordopskrifte en -liggaam sonder enige ontsnapping of enkripsie, so in hierdie geval hoef ons nie eers die standaardbiblioteek te gebruik nie.

Stroom logboeke vanaf ClickHouse

ClickHouse het vir 'n relatief lang tyd (sedert 2019?) geoptimaliseer vir navrae met ORDER BY, dus 'n navraag soos

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

Dit sal onmiddellik begin om reëls terug te stuur wat die substring "iets" in hul boodskap het, sonder om te wag dat die skandering voltooi is.

Dit sal ook baie gerieflik wees as ClickHouse self die versoek gekanselleer het toe die verbinding daarmee gesluit is, maar dit is nie die verstekgedrag nie. Outomatiese versoekkansellasie kan geaktiveer word deur die opsie te gebruik cancel_http_readonly_queries_on_client_close=1.

Korrekte hantering van SIGPIPE in Go

Wanneer jy, sê, die opdrag uitvoer some_cmd | head -n 10, presies hoe die opdrag some_cmd stop uitvoering wanneer head 10 reëls afgetrek? Die antwoord is eenvoudig: wanneer head eindig, die pyp sluit, en die stdout van die some_cmd-opdrag begin voorwaardelik "na nêrens" wys. Wanneer some_cmd probeer om na 'n toe pyp te skryf, dit ontvang 'n SIGPIPE-sein, wat die program by verstek stilweg beëindig.

In Go gebeur dit ook by verstek, maar die SIGPIPE seinhanteerder druk ook "sein: SIGPIPE" of 'n soortgelyke boodskap aan die einde, en om hierdie boodskap uit te vee moet ons net SIGPIPE self hanteer soos ons wil, dit wil sê net stilweg uitgang:

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

Wys boodskapkonteks

Dikwels wil jy die konteks sien waarin een of ander fout plaasgevind het (byvoorbeeld watter versoek paniek veroorsaak het, of watter verwante probleme sigbaar was voor die ongeluk), en in grep Dit word gedoen deur die -A-, -B- en -C-opsies te gebruik, wat die gespesifiseerde aantal reëls onderskeidelik ná, voor en om die boodskap wys.

Ongelukkig het ek nie 'n maklike manier gevind om dieselfde in ClickHouse te doen nie, so om die konteks te vertoon, word 'n bykomende versoek soos hierdie na elke reël van die resultaat gestuur (die besonderhede hang af van die sortering en of die konteks voor gewys word of daarna):

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

Aangesien die versoek amper onmiddellik gestuur word nadat ClickHouse die ooreenstemmende lyn terugstuur, beland dit in die kas en oor die algemeen word die versoek redelik vinnig uitgevoer en verbruik 'n bietjie SVE (gewoonlik neem die versoek ongeveer ~6 ms op my virtuele masjien).

Wys nuwe boodskappe intyds

Om inkomende boodskappe in (amper) reële tyd te wys, voer ons eenvoudig die versoek een keer elke paar sekondes uit, en onthou die laaste tydstempel wat ons voorheen teëgekom het.

Bevelvoorbeelde

Hoe lyk tipiese logscli-opdragte in die praktyk?

As u die Amazon-datastel afgelaai het wat ek aan die begin van die artikel genoem het, kan u die volgende opdragte uitvoer:

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

verwysings

Die nutskode (sonder dokumentasie) is beskikbaar op github by https://github.com/YuriyNasretdinov/logscli. Ek sal bly wees om jou gedagtes te hoor oor my idee vir 'n konsole-koppelvlak om logs gebaseer op ClickHouse te bekyk.

Bron: will.com

Voeg 'n opmerking