Ние разработваме най-удобния интерфейс в света* за преглед на дневници

Ние разработваме най-удобния интерфейс в света* за преглед на дневници Ако някога сте използвали уеб интерфейси за преглед на регистрационни файлове, вероятно сте забелязали как по правило тези интерфейси са тромави и (често) не много удобни и отзивчиви. С някои можете да свикнете, някои са абсолютно ужасни, но ми се струва, че причината за всички проблеми е, че подхождаме неправилно към задачата да преглеждаме регистрационни файлове: опитваме се да създадем уеб интерфейс, където CLI (интерфейс на командния ред) работи по-добре. Аз лично се чувствам много удобно да работя с tail, grep, awk и други и затова за мен идеалният интерфейс за работа с регистрационни файлове би бил нещо подобно на tail и grep, но което може да се използва и за четене на регистрационни файлове, които идват от много сървъри. Това е, разбира се, прочетете ги от ClickHouse!

*по лично мнение на потребителя на habra супер си

Запознайте се с logscli

Не измислих име за моя интерфейс и, честно казано, той по-скоро съществува под формата на прототип, но ако искате веднага да видите изходния код, тогава сте добре дошли: https://github.com/YuriyNasretdinov/logscli (350 реда избран Go код).

Възможности

Целта ми беше да направя интерфейс, който да изглежда познат на тези, които са свикнали с tail/grep, тоест да поддържа следните неща:

  1. Вижте всички регистрационни файлове без филтриране.
  2. Оставете редове, съдържащи фиксиран подниз (флаг -F у grep).
  3. Оставете редове, които съответстват на регулярния израз (флаг -E у grep).
  4. По подразбиране прегледът е в обратен хронологичен ред, тъй като най-новите регистрационни файлове обикновено представляват интерес първи.
  5. Показване на контекст до всеки ред (опции -A, -B и -C у grep, отпечатване на N реда преди, след и около всеки съответстващ ред, съответно).
  6. Преглеждайте входящите регистрационни файлове в реално време, със или без филтриране (по същество tail -f | grep).
  7. Интерфейсът трябва да е съвместим с less, head, tail и други - по подразбиране резултатите трябва да се връщат без ограничения в броя им; редовете се отпечатват като поток, стига потребителят да е заинтересован да ги получи; сигнал SIGPIPE трябва безшумно да прекъсва поточно предаване на журнал, точно както правят tail, grep и други UNIX помощни програми.

Изпълнение

Предполагам, че вече по някакъв начин знаете как да доставяте регистрационни файлове на ClickHouse. Ако не, препоръчвам да опитате наркотик и котешка къщаИ тази статия за доставката на регистрационни файлове.

Първо трябва да вземете решение за основната схема. Тъй като обикновено искате да получавате журнали, сортирани по време, изглежда логично да ги съхранявате по този начин. Ако има много категории журнали и всички те са от един и същи тип, тогава можете да направите категория журнал като първа колона на първичния ключ - това ще ви позволи да имате една таблица вместо няколко, което ще бъде голям плюс, когато вмъкване в ClickHouse (на сървъри с твърди дискове се препоръчва да вмъквате данни не повече от ~1 пъти в секунда за целия сървър).

Тоест имаме нужда от приблизително следната схема на таблицата:

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

За съжаление, не можах веднага да намеря никакви отворени източници с реалистични регистрационни файлове, които мога да взема и изтегля, така че вместо това взех това като пример прегледи на продукти от Amazon до 2015 г. Разбира се, тяхната структура не е точно същата като тази на текстовите регистрационни файлове, но за илюстрация това не е важно.

инструкции за качване на рецензии от Amazon в ClickHouse

Нека създадем таблица:

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

В набора от данни на Amazon има само дата за преглед, но няма точен час, така че нека попълним тези данни със случайност.

Не е нужно да изтегляте всички tsv файлове и да се ограничавате до първите ~10-20, за да получите доста голям набор от данни, които няма да се поберат в 16 GB RAM. За да кача TSV файлове използвах следната команда:

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

На стандартен постоянен диск (който е HDD) в Google Cloud с размер 1000 GB (взех този размер главно, за да е малко по-висока скоростта, въпреки че може би SSD с необходимия размер би бил по-евтин) качването скоростта беше приблизително ~ 75 MB/sec на 4 ядра.

  • Трябва да направя резервация, че работя в Google, но използвах личен акаунт и тази статия няма нищо общо с работата ми в компанията

Ще направя всички илюстрации с този конкретен набор от данни, тъй като това е всичко, което имах под ръка.

Показване на напредъка на сканирането на данни

Тъй като в ClickHouse ще използваме пълно сканиране на таблица с регистрационни файлове и тази операция може да отнеме значително време и може да не даде никакви резултати дълго време, ако бъдат намерени малко съвпадения, препоръчително е да можете да показвате прогрес на заявката до получаване на първите редове с резултата. За да направите това, има параметър в HTTP интерфейса, който ви позволява да изпращате прогрес в HTTP заглавки: send_progress_in_http_headers=1. За съжаление, стандартната библиотека Go не може да чете заглавки, когато са получени, но интерфейсът HTTP 1.0 (да не се бърка с 1.1!) се поддържа от ClickHouse, така че можете да отворите необработена TCP връзка към ClickHouse и да я изпратите там GET /?query=... HTTP/1.0nn и получаваме заглавките и тялото на отговора без екраниране или криптиране, така че в този случай дори не е необходимо да използваме стандартната библиотека.

Поточно предаване на регистрационни файлове от ClickHouse

ClickHouse има оптимизация за заявки с ORDER BY от сравнително дълго време (от 2019 г.?), така че заявка като

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

Веднага ще започне да връща редове, които имат подниза "нещо" в съобщението си, без да чака сканирането да приключи.

Също така, би било много удобно, ако самата ClickHouse отмени заявката, когато връзката с нея беше затворена, но това не е поведението по подразбиране. Автоматичното анулиране на заявка може да бъде активирано чрез опцията cancel_http_readonly_queries_on_client_close=1.

Правилно боравене със SIGPIPE в Go

Когато изпълните, да речем, командата some_cmd | head -n 10, точно как командата some_cmd спира изпълнението, когато head извади 10 реда? Отговорът е прост: кога head приключва, тръбата се затваря и стандартният изход на командата some_cmd започва да сочи, условно, „никъде“. Кога some_cmd опитва се да пише в затворена тръба, той получава сигнал SIGPIPE, който безшумно прекратява програмата по подразбиране.

В Go това също се случва по подразбиране, но манипулаторът на сигнали SIGPIPE също отпечатва "сигнал: SIGPIPE" или подобно съобщение в края и за да изчистим това съобщение, ние просто трябва сами да боравим със SIGPIPE по начина, по който искаме, тоест просто безшумно изход:

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

Показване на контекста на съобщението

Често искате да видите контекста, в който е възникнала някаква грешка (например коя заявка е причинила паника или какви свързани проблеми са били видими преди срива) и в grep Това става с помощта на опциите -A, -B и -C, които показват определения брой редове съответно след, преди и около съобщението.

За съжаление, не намерих лесен начин да направя същото в ClickHouse, така че за показване на контекста се изпраща допълнителна заявка като тази към всеки ред от резултата (подробностите зависят от сортирането и дали контекстът е показан преди или след):

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

Тъй като заявката се изпраща почти веднага след като ClickHouse върне съответния ред, тя се озовава в кеша и като цяло заявката се изпълнява доста бързо и консумира малко CPU (обикновено заявката отнема около ~6 ms на моята виртуална машина).

Показвайте нови съобщения в реално време

За да покажем входящите съобщения в (почти) реално време, ние просто изпълняваме заявката веднъж на всеки няколко секунди, като си спомняме последното времево клеймо, което сме срещали преди.

Примери за команди

Как изглеждат типичните logscli команди на практика?

Ако сте изтеглили набора от данни на Amazon, който споменах в началото на статията, можете да изпълните следните команди:

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

Позоваването

Кодът на помощната програма (без документация) е достъпен в github на адрес https://github.com/YuriyNasretdinov/logscli. Ще се радвам да чуя вашите мисли относно моята идея за конзолен интерфейс за преглед на логове, базиран на ClickHouse.

Източник: www.habr.com

Добавяне на нов коментар