Jeśli kiedykolwiek korzystałeś z interfejsów internetowych do przeglądania logów, prawdopodobnie zauważyłeś, że interfejsy te są z reguły uciążliwe i (często) niezbyt wygodne i responsywne. Do niektórych można się przyzwyczaić, niektóre są absolutnie okropne, ale wydaje mi się, że przyczyną wszystkich problemów jest to, że niepoprawnie podchodzimy do zadania przeglądania logów: staramy się stworzyć interfejs sieciowy, w którym CLI (interfejs wiersza poleceń) działa lepiej. Osobiście bardzo dobrze czuję się w pracy z tailem, grepem, awk i innymi, dlatego dla mnie idealnym interfejsem do pracy z logami byłoby coś podobnego do taila i grep, ale którego można by również używać do czytania logów pochodzących z wielu serwerów. Oznacza to oczywiście, że przeczytasz je w ClickHouse!
*według osobistej opinii użytkownika Habra
Poznaj logscli
Nazwy dla mojego interfejsu nie wymyśliłem i, szczerze mówiąc, istnieje ona raczej w formie prototypu, ale jeśli chcesz od razu zobaczyć kod źródłowy, to nie ma za co:
Możliwości
Moim celem było stworzenie interfejsu, który wydawałby się znajomy dla tych, którzy są przyzwyczajeni do tail/grep, to znaczy do obsługi następujących rzeczy:
- Przeglądaj wszystkie logi, bez filtrowania.
- Pozostaw linie zawierające stały podciąg (flaga
-F
уgrep
). - Pozostaw linie pasujące do wyrażenia regularnego (flaga
-E
уgrep
). - Domyślnie przeglądanie odbywa się w odwrotnej kolejności chronologicznej, ponieważ najnowsze logi są zazwyczaj interesujące w pierwszej kolejności.
- Pokaż kontekst obok każdej linii (options
-A
,-B
и-C
уgrep
, wypisując N linii odpowiednio przed, po i wokół każdej pasującej linii). - Przeglądaj przychodzące logi w czasie rzeczywistym, z filtrowaniem lub bez (zasadniczo
tail -f | grep
). - Interfejs musi być kompatybilny z
less
,head
,tail
i inne - domyślnie wyniki powinny być zwracane bez ograniczeń co do ich liczby; linie drukowane są strumieniowo, o ile użytkownik jest zainteresowany ich otrzymaniem; sygnałSIGPIPE
powinny po cichu przerywać przesyłanie dzienników, tak jak to robiątail
,grep
i inne narzędzia UNIX.
realizacja
Zakładam, że już w jakiś sposób wiesz, jak dostarczać logi do ClickHouse. Jeśli nie, polecam spróbować
Najpierw musisz zdecydować o schemacie podstawowym. Ponieważ zazwyczaj chcesz otrzymywać logi posortowane według czasu, logiczne wydaje się przechowywanie ich w ten sposób. Jeżeli kategorii logów jest wiele i wszystkie są tego samego typu, to możesz ustawić kategorię logu jako pierwszą kolumnę klucza podstawowego - dzięki temu będziesz mieć jedną tabelę zamiast kilku, co będzie dużym plusem, gdy wstawianie do ClickHouse (na serwerach z dyskami twardymi zaleca się wstawianie danych nie częściej niż ~1 razy na sekundę dla całego serwera).
Oznacza to, że potrzebujemy w przybliżeniu następującego schematu tabeli:
CREATE TABLE logs(
category LowCardinality(String), -- категория логов (опционально)
time DateTime, -- время события
millis UInt16, -- миллисекунды (могут быть и микросекунды, и т.д.): рекомендуется хранить, если событий много, чтобы было легче различать события между собой
..., -- ваши собственные поля, например имя сервера, уровень логирования, и так далее
message String -- текст сообщения
) ENGINE=MergeTree()
ORDER BY (category, time, millis)
Niestety, nie mogłem od razu znaleźć żadnych otwartych źródeł z realistycznymi logami, które mógłbym pobrać i pobrać, więc wziąłem to jako przykład
instrukcje dotyczące przesyłania recenzji Amazon do ClickHouse
Stwórzmy 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
W zbiorze danych Amazona jest tylko data przeglądu, ale nie ma dokładnej godziny, więc uzupełnijmy te dane randonem.
Nie musisz pobierać wszystkich plików tsv i ograniczać się do pierwszych ~10-20, aby uzyskać dość duży zestaw danych, który nie zmieści się w 16 GB RAM-u. Aby przesłać pliki TSV, użyłem następującego polecenia:
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
Na standardowym dysku trwałym (czyli HDD) w Google Cloud o pojemności 1000 GB (ten rozmiar wziąłem głównie po to, żeby prędkość była trochę większa, chociaż być może dysk SSD o wymaganej wielkości byłby tańszy) przesyłanie prędkość wynosiła około ~ 75 MB/s na 4 rdzeniach.
- Muszę zastrzec, że pracuję w Google, ale korzystałem z konta osobistego i ten artykuł nie ma nic wspólnego z moją pracą w firmie
Wszystkie ilustracje wykonam z wykorzystaniem tego konkretnego zbioru danych, ponieważ to wszystko, co miałem pod ręką.
Pokaż postęp skanowania danych
Ponieważ w ClickHouse zastosujemy pełne skanowanie tabeli z logami, a operacja ta może zająć znaczną ilość czasu i może nie dać żadnych rezultatów przez długi czas w przypadku znalezienia kilku dopasowań, wskazane jest, aby móc wyświetlić postęp zapytania do momentu otrzymania pierwszych wierszy z wynikiem. W tym celu w interfejsie HTTP znajduje się parametr pozwalający na przesyłanie postępu w nagłówkach HTTP: send_progress_in_http_headers=1
. Niestety, standardowa biblioteka Go nie może czytać nagłówków w momencie ich otrzymania, ale interfejs HTTP 1.0 (nie mylić z 1.1!) jest obsługiwany przez ClickHouse, więc możesz otworzyć surowe połączenie TCP z ClickHouse i wysłać je tam GET /?query=... HTTP/1.0nn
i odbieraj nagłówki i treść odpowiedzi bez ucieczki lub szyfrowania, więc w tym przypadku nie musimy nawet korzystać ze standardowej biblioteki.
Dzienniki przesyłania strumieniowego z ClickHouse
ClickHouse ma optymalizację dla zapytań z ORDER BY od stosunkowo długiego czasu (od 2019?), więc zapytanie takie jak
SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESC
Natychmiast zacznie zwracać linie zawierające podciąg „coś” w swojej wiadomości, bez czekania na zakończenie skanowania.
Ponadto byłoby bardzo wygodne, gdyby ClickHouse sam anulował żądanie po zamknięciu połączenia z nim, ale nie jest to zachowanie domyślne. Za pomocą opcji można włączyć automatyczne anulowanie żądania cancel_http_readonly_queries_on_client_close=1
.
Prawidłowa obsługa SIGPIPE w Go
Kiedy wykonasz, powiedzmy, polecenie some_cmd | head -n 10
, dokładnie jak polecenie some_cmd
zatrzymuje wykonywanie, gdy head
odjęto 10 linii? Odpowiedź jest prosta: kiedy head
kończy się, potok się zamyka, a standardowe wyjście polecenia Some_cmd zaczyna warunkowo wskazywać „donikąd”. Gdy some_cmd
próbuje pisać do zamkniętego potoku,
W Go również dzieje się to domyślnie, ale procedura obsługi sygnału SIGPIPE wypisuje również na końcu „sygnał: SIGPIPE” lub podobny komunikat, a aby wyczyścić ten komunikat, musimy po prostu sami obsłużyć SIGPIPE tak, jak chcemy, to znaczy po prostu po cichu Wyjście:
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGPIPE)
go func() {
<-ch
os.Exit(0)
}()
Pokaż kontekst wiadomości
Często chcesz zobaczyć kontekst, w którym wystąpił jakiś błąd (na przykład, które żądanie spowodowało panikę lub jakie powiązane problemy były widoczne przed awarią), a w grep
Dokonuje się tego za pomocą opcji -A, -B i -C, które pokazują określoną liczbę wierszy odpowiednio po, przed i wokół komunikatu.
Niestety nie znalazłem łatwego sposobu na zrobienie tego samego w ClickHouse, więc aby wyświetlić kontekst, do każdej linii wyniku wysyłane jest dodatkowe żądanie tego typu (szczegóły zależą od sortowania i tego, czy kontekst był pokazywany wcześniej lub później):
SELECT time,millis,review_body FROM amazon
WHERE (time = 'ВРЕМЯ_СОБЫТИЯ' AND millis < МИЛЛИСЕКУНДЫ_СОБЫТИЯ) OR (time < 'ВРЕМЯ_СОБЫТИЯ')
ORDER BY time DESC, millis DESC
LIMIT КОЛИЧЕСТВО_СТРОК_КОНТЕКСТА
SETTINGS max_threads=1
Ponieważ żądanie jest wysyłane niemal natychmiast po zwróceniu przez ClickHouse odpowiedniej linii, trafia do pamięci podręcznej i ogólnie żądanie jest wykonywane dość szybko i zużywa trochę procesora (zwykle żądanie zajmuje około ~6 ms na mojej maszynie wirtualnej).
Pokazuj nowe wiadomości w czasie rzeczywistym
Aby wyświetlać przychodzące wiadomości w (prawie) czasie rzeczywistym, po prostu wykonujemy żądanie raz na kilka sekund, zapamiętując ostatni znacznik czasu, który napotkaliśmy wcześniej.
Przykłady poleceń
Jak w praktyce wyglądają typowe polecenia logscli?
Jeśli pobrałeś zbiór danych Amazon, o którym wspomniałem na początku artykułu, możesz uruchomić następujące polecenia:
# Показать строки, где встречается слово 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
referencje
Kod narzędzia (bez dokumentacji) jest dostępny na githubie pod adresem
Źródło: www.habr.com