Ginagawa namin ang pinaka-maginhawang interface sa mundo* para sa pagtingin sa mga log

Ginagawa namin ang pinaka-maginhawang interface sa mundo* para sa pagtingin sa mga log Kung nakagamit ka na ng mga web interface upang tingnan ang mga log, malamang na napansin mo kung paano, bilang panuntunan, ang mga interface na ito ay masalimuot at (kadalasan) hindi masyadong maginhawa at tumutugon. Ang ilan ay maaari mong masanay, ang ilan ay talagang kakila-kilabot, ngunit tila sa akin na ang dahilan para sa lahat ng mga problema ay ang paglapit namin sa gawain ng pagtingin sa mga log nang hindi tama: sinusubukan naming lumikha ng isang web interface kung saan ang CLI (command line interface) mas gumagana. Ako mismo ay komportable na magtrabaho kasama ang buntot, grep, awk at iba pa, at samakatuwid para sa akin ang perpektong interface para sa pagtatrabaho sa mga log ay magiging katulad ng buntot at grep, ngunit maaari ding gamitin upang basahin ang mga log na nagmula sa maraming mga server . Iyon ay, siyempre, basahin ang mga ito mula sa ClickHouse!

*ayon sa personal na opinyon ng gumagamit ng habra astig ka

Kilalanin ang logscli

Hindi ako nakabuo ng isang pangalan para sa aking interface, at, sa totoo lang, ito ay umiiral sa anyo ng isang prototype, ngunit kung gusto mong agad na makita ang source code, pagkatapos ay malugod kang tinatanggap: https://github.com/YuriyNasretdinov/logscli (350 linya ng napiling Go code).

Mga Kakayahan

Ang layunin ko ay gumawa ng interface na mukhang pamilyar sa mga nakasanayan nang mag-tail/grep, iyon ay, upang suportahan ang mga sumusunod na bagay:

  1. Tingnan ang lahat ng mga log, nang walang pag-filter.
  2. Mag-iwan ng mga linyang naglalaman ng nakapirming substring (flag -F у grep).
  3. Mag-iwan ng mga linya na tumutugma sa regular na expression (flag -E у grep).
  4. Bilang default, ang pagtingin ay nasa reverse chronological na pagkakasunud-sunod, dahil ang pinakakamakailang mga log ay kadalasang interesado muna.
  5. Ipakita ang konteksto sa tabi ng bawat linya (mga opsyon -A, -B и -C у grep, pag-print ng N linya bago, pagkatapos, at sa paligid ng bawat pagtutugmang linya, ayon sa pagkakabanggit).
  6. Tingnan ang mga papasok na log sa real time, mayroon o walang pag-filter (sa totoo lang tail -f | grep).
  7. Ang interface ay dapat na tugma sa less, head, tail at iba pa - bilang default, ang mga resulta ay dapat ibalik nang walang mga paghihigpit sa kanilang numero; ang mga linya ay naka-print bilang isang stream hangga't ang gumagamit ay interesado sa pagtanggap ng mga ito; hudyat SIGPIPE dapat na tahimik na matakpan ang pag-stream ng log, tulad ng ginagawa nila tail, grep at iba pang mga kagamitan sa UNIX.

Pagpapatupad

Ipagpalagay ko na kahit papaano ay alam mo na kung paano maghatid ng mga log sa ClickHouse. Kung hindi, inirerekomenda kong subukan ito lsd и kittenhouseAt ang artikulong ito tungkol sa paghahatid ng log.

Una kailangan mong magpasya sa base scheme. Dahil karaniwang gusto mong makatanggap ng mga log na pinagsunod-sunod ayon sa oras, tila lohikal na iimbak ang mga ito sa ganoong paraan. Kung maraming mga kategorya ng log at pareho silang lahat, maaari kang gumawa ng kategorya ng log bilang unang hanay ng pangunahing susi - magbibigay-daan ito sa iyo na magkaroon ng isang talahanayan sa halip na ilan, na magiging isang malaking plus kapag pagpasok sa ClickHouse (sa mga server na may mga hard drive, inirerekumenda na magpasok ng data nang hindi hihigit sa ~1 beses bawat segundo para sa buong server).

Iyon ay, kailangan namin ng humigit-kumulang sa sumusunod na scheme ng talahanayan:

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

Sa kasamaang palad, hindi ako agad na makahanap ng anumang mga bukas na mapagkukunan na may makatotohanang mga log na maaari kong makuha at i-download, kaya kinuha ko ito bilang isang halimbawa mga review ng mga produkto mula sa Amazon bago ang 2015. Siyempre, ang kanilang istraktura ay hindi eksaktong kapareho ng sa mga log ng teksto, ngunit para sa mga layunin ng paglalarawan na ito ay hindi mahalaga.

mga tagubilin para sa pag-upload ng mga review ng Amazon sa ClickHouse

Gumawa tayo ng talahanayan:

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

Sa dataset ng Amazon mayroon lamang isang petsa para sa isang pagsusuri, ngunit walang eksaktong oras, kaya punan natin ang data na ito ng isang randon.

Hindi mo kailangang i-download ang lahat ng tsv file at limitahan ang iyong sarili sa unang ~10-20 para makakuha ng medyo malaking set ng data na hindi magkakasya sa 16 GB ng RAM. Upang mag-upload ng mga TSV file ginamit ko ang sumusunod na command:

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

Sa isang karaniwang Persistent Disk (na isang HDD) sa Google Cloud na may sukat na 1000 GB (Kinuha ko ang laki na ito pangunahin upang ang bilis ay mas mataas ng kaunti, bagaman marahil ang isang SSD ng kinakailangang laki ay mas mura) ang pag-upload ang bilis ay humigit-kumulang ~ 75 MB/sec sa 4 na mga core.

  • Dapat akong magpareserba na nagtatrabaho ako sa Google, ngunit gumamit ako ng personal na account at ang artikulong ito ay walang kinalaman sa aking trabaho sa kumpanya

Gagawa ako ng lahat ng mga guhit na may ganitong partikular na dataset, dahil ito lang ang nasa kamay ko.

Ipakita ang progreso ng pag-scan ng data

Dahil sa ClickHouse gagamit kami ng isang buong pag-scan sa isang mesa na may mga log, at ang operasyong ito ay maaaring tumagal ng isang malaking halaga ng oras at maaaring hindi magbunga ng anumang mga resulta sa loob ng mahabang panahon kung ilang mga tugma ang natagpuan, ipinapayong maipakita ang progreso ng query hanggang sa matanggap ang mga unang row na may resulta. Upang gawin ito, mayroong isang parameter sa interface ng HTTP na nagbibigay-daan sa iyong magpadala ng pag-unlad sa mga header ng HTTP: send_progress_in_http_headers=1. К сожалению, стандартная библиотека Go не умеет читать заголовки по мере их получения, но интерфейс HTTP 1.0 (не путайте с 1.1!) поддерживается ClickHouse, поэтому можно открыть сырое TCP-соединение с ClickHouse, послать туда GET /?query=... HTTP/1.0nn at tanggapin ang mga header at body ng tugon nang walang anumang pagtakas o pag-encrypt, kaya sa kasong ito, hindi na namin kailangan pang gamitin ang karaniwang library.

Pag-stream ng mga log mula sa ClickHouse

Ang ClickHouse ay nagkaroon ng pag-optimize para sa mga query na may ORDER BY sa medyo mahabang panahon (mula noong 2019?), kaya isang query tulad ng

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

Ito ay magsisimula kaagad sa pagbabalik ng mga linya na mayroong substring na "something" sa kanilang mensahe, nang hindi naghihintay na matapos ang pag-scan.

Gayundin, magiging maginhawa kung kinansela mismo ng ClickHouse ang kahilingan kapag sarado ang koneksyon dito, ngunit hindi ito ang default na gawi. Maaaring paganahin ang awtomatikong pagkansela ng kahilingan gamit ang opsyon cancel_http_readonly_queries_on_client_close=1.

Tamang pangangasiwa ng SIGPIPE sa Go

Kapag nag-execute ka, sabihin mo, ang command some_cmd | head -n 10, eksakto kung paano ang utos some_cmd hihinto ang pagpapatupad kapag head nagbawas ng 10 linya? Ang sagot ay simple: kailan head magtatapos, magsasara ang tubo, at ang stdout ng some_cmd na utos ay magsisimulang tumuro, na may kondisyon, "sa kahit saan". Kailan some_cmd sinusubukang sumulat sa isang saradong tubo, tumatanggap ito ng signal ng SIGPIPE, na tahimik na tinatapos ang programa bilang default.

Sa Go, nangyayari rin ito bilang default, ngunit ang SIGPIPE signal handler ay nagpi-print din ng "signal: SIGPIPE" o isang katulad na mensahe sa dulo, at para i-clear ang mensaheng ito kailangan lang nating hawakan ang SIGPIPE sa paraang gusto natin, iyon ay, tahimik lang. labasan:

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

Ipakita ang konteksto ng mensahe

Kadalasan gusto mong makita ang konteksto kung saan nagkaroon ng ilang error (halimbawa, kung aling kahilingan ang nagdulot ng gulat, o kung anong mga nauugnay na problema ang nakikita bago ang pag-crash), at sa grep Ginagawa ito gamit ang -A, -B at -C na mga opsyon, na nagpapakita ng tinukoy na bilang ng mga linya pagkatapos, bago, at sa paligid ng mensahe, ayon sa pagkakabanggit.

Sa kasamaang palad, hindi ako nakahanap ng madaling paraan upang gawin ang parehong sa ClickHouse, kaya upang ipakita ang konteksto, isang karagdagang kahilingan tulad nito ay ipinadala sa bawat linya ng resulta (ang mga detalye ay depende sa pag-uuri at kung ang konteksto ay ipinapakita bago o pagkatapos):

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

Dahil ang kahilingan ay ipinadala halos kaagad pagkatapos ibalik ng ClickHouse ang kaukulang linya, napupunta ito sa cache at sa pangkalahatan ang kahilingan ay naisakatuparan nang mabilis at kumonsumo ng kaunting CPU (karaniwang ang kahilingan ay tumatagal ng humigit-kumulang ~6 ms sa aking virtual machine).

Magpakita ng mga bagong mensahe sa real time

Upang maipakita ang mga papasok na mensahe sa (halos) real time, isasagawa lang namin ang kahilingan nang isang beses bawat ilang segundo, na inaalala ang huling timestamp na nakatagpo namin dati.

Mga halimbawa ng utos

Ano ang hitsura ng mga tipikal na utos ng logscli sa pagsasanay?

Kung na-download mo ang dataset ng Amazon na binanggit ko sa simula ng artikulo, maaari mong patakbuhin ang mga sumusunod na command:

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

sanggunian

Ang utility code (nang walang dokumentasyon) ay makukuha sa github sa https://github.com/YuriyNasretdinov/logscli. Natutuwa akong marinig ang iyong mga saloobin sa aking ideya para sa isang console interface para sa pagtingin sa mga log batay sa ClickHouse.

Pinagmulan: www.habr.com

Magdagdag ng komento