Opracowujemy najwygodniejszy na świecie* interfejs do przeglądania logów

Opracowujemy najwygodniejszy na świecie* interfejs do przeglądania logów Jeśli kiedykolwiek korzystałeś z interfejsów internetowych do przeglądania logów, to na pewno zauważyłeś, jak zazwyczaj są one nieporęczne i (często) niezbyt wygodne oraz responsywne. Do niektórych można się przyzwyczaić, niektóre są całkowicie okropne, ale moim zdaniem źródłem wszystkich problemów jest to, że źle podchodzimy do zadania przeglądania logów: próbujemy stworzyć interfejs webowy tam, gdzie lepiej sprawdza się CLI (interfejs wiersza poleceń). Osobiście bardzo komfortowo pracuję z tail, grep, awk i innymi, więc moim idealnym interfejsem do pracy z logami byłaby coś podobnego do tail i grep, ale które można by używać do czytania logów z wielu serwerów. Oczywiście czytać je z ClickHouse!

*według osobistej opinii użytkownika Habr youROCK

Poznaj logscli

Nie wymyśliłem nazwy dla swojego interfejsu, i szczerze mówiąc, istnieje on raczej w formie prototypu, ale jeśli chcesz od razu zobaczyć źródła, to zapraszam: https://github.com/YuriyNasretdinov/logscli (350 linii starannie wybranych kodów w Go).

Możliwości

Moim celem było stworzenie interfejsu, który będzie przypominał tym, którzy są przyzwyczajeni do tail/grep, czyli wspierać następujące rzeczy:

  1. Przeglądanie wszystkich logów, bez filtrowania.
  2. Zachowanie linii, które zawierają ustaloną podciąg (flaga -F u grep).
  3. Zachowanie linii, które pasują do wyrażenia regularnego (flaga -E u grep).
  4. Domyślnie przeglądanie w odwrotnym porządku chronologicznym, ponieważ zazwyczaj najpierw interesują nas najnowsze logi.
  5. Wyświetlanie kontekstu wokół każdej linii (parametry -A, -B i -C u grep, wyświetlające N linii przed, po i wokół każdej pasującej linii odpowiednio).
  6. Przeglądanie przychodzących logów w czasie rzeczywistym, z filtrowaniem i bez (w istocie tail -f | grep).
  7. Interfejs powinien być zgodny z less, head, tail i innymi - domyślnie powinny być zwracane wyniki bez ograniczeń co do ich liczby; linie są wyświetlane strumieniowo, dopóki użytkownik jest zainteresowany ich otrzymywaniem; sygnał SIGPIPE powinien cicho przerywać strumieniowanie logów, tak jak robią to tail, grep i inne narzędzia UNIX-owe.

Realizacja

Zakładam, że już w jakiś sposób potrafisz dostarczać logi do ClickHouse. Jeśli nie, zalecam spróbować lsd i kittenhouse, a także tego artykułu o dostarczaniu logów.

Na początek trzeba określić schemat bazy. Ponieważ logi zazwyczaj chcemy otrzymywać posortowane według czasu, logiczne wydaje się ich takie przechowywanie. Jeśli jest wiele kategorii logów i są one jednorodne, to jako pierwszy element klucza głównego można zastosować kategorię logów — to pozwoli mieć jedną tabelę zamiast kilku, co przy wstawianiu danych do ClickHouse będzie dużym plusem (na serwerach z dyskami twardymi zaleca się wstawianie danych nie częściej niż ~1 raz na sekundę. na cały serwer).

To znaczy, potrzebujemy mniej więcej takiego schematu tabel:

CREATE TABLE logs(
    category LowCardinality(String), -- kategoria logów (opcjonalnie)
    time DateTime, -- czas zdarzenia
    millis UInt16, -- milisekundy (mogą być również mikrosekundy itp.): zaleca się przechowywać, jeśli zdarzeń jest dużo, aby łatwiej odróżniać je od siebie
    ..., -- twoje własne pola, na przykład nazwa serwera, poziom logowania itp.
    message String -- treść wiadomości
) ENGINE=MergeTree()
ORDER BY (category, time, millis)

Niestety, nie mogłem od razu znaleźć żadnych otwartych źródeł z realistycznymi logami, które można by pobrać, dlatego wziąłem przykład z recenzji produktów z Amazonu sprzed 2015 roku.. Oczywiście ich struktura nie jest dokładnie taka sama jak w logach tekstowych, ale dla ilustracji nie jest to zasadniczo istotne.

instrukcja do zaimportowania 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 zestawie danych Amazona jest tylko data recenzji, ale nie ma dokładnego czasu, więc wypełnimy te dane losowo.

Nie trzeba pobierać wszystkich plików tsv i można ograniczyć się do pierwszych ~10-20, aby uzyskać wystarczający zbiór danych, który nie zmieści się w 16 GB pamięci RAM. Do załadowania plików 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 Persistent Disk (HDD) w Google Cloud o pojemności 1000 GB (wybrałem ten rozmiar głównie po to, aby zwiększyć prędkość, chociaż być może SSD o odpowiedniej pojemności byłby tańszy) prędkość przesyłania wyniosła około ~75 MB/s na 4 rdzeniach.

  • Muszę zaznaczyć, że pracuję w Google, ale korzystałem z prywatnego konta i ten artykuł nie ma związku z moją pracą w firmie.

Wszystkie ilustracje będę wykonywać z tym zestawem danych, ponieważ to wszystko, co miałem pod ręką.

Wyświetlanie postępu skanowania danych

Ponieważ w ClickHouse będziemy używać pełnego skanowania tabeli z logami, a ta operacja może zająć znaczną ilość czasu i długo nie zwracać żadnych wyników, jeśli znaleziono niewiele zgodności, warto umieć pokazywać postęp wykonywania zapytania do momentu uzyskania pierwszych wierszy z wynikiem. W tym celu w interfejsie HTTP jest parametr, który umożliwia zwracanie postępu w nagłówkach HTTP: send_progress_in_http_headers=1. Niestety, standardowa biblioteka Go nie potrafi odczytywać nagłówków w miarę ich otrzymywania, ale interfejs HTTP 1.0 (nie mylić z 1.1!) jest obsługiwany przez ClickHouse, więc można otworzyć surowe połączenie TCP z ClickHouse, wysłać tam GET \/?query=... HTTP\/1.0nn i uzyskać w odpowiedzi nagłówki oraz treść odpowiedzi bez jakiejkolwiek obróbki czy szyfrowania, więc w tym przypadku nie musimy korzystać ze standardowej biblioteki.

Streaming logów z ClickHouse

W ClickHouse istnieje już od dłuższego czasu (od 2019 roku?) optymalizacja dla zapytań z ORDER BY, więc zapytanie w postaci

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

zacznie od razu zwracać wiersze, w których w komunikacie znajduje się podciąg "something", nie czekając na zakończenie skanowania.

Również byłoby bardzo wygodnie, gdyby ClickHouse samodzielnie anulował zapytanie, gdy połączenie zostało zamknięte, ale to nie jest domyślne zachowanie. Automatyczne anulowanie zapytania można włączyć opcją cancel_http_readonly_queries_on_client_close=1.

Prawidłowe przetwarzanie SIGPIPE w Go

Kiedy wykonujesz, powiedzmy, polecenie some_cmd | head -n 10, w jaki sposób polecenie some_cmd kończy swoje wykonanie, gdy head odczytał 10 wierszy? Odpowiedź jest prosta: gdy head kończy się, pipe zamyka się, a stdout polecenia some_cmd zaczyna wskazywać, umownie, «donikąd». Kiedy some_cmd stara się zapisać do zamkniętego pipe’a, otrzymuje sygnał SIGPIPE, który domyślnie cicho kończy program..

W Go dzieje się to domyślnie, ale obsługa sygnału SIGPIPE również wyświetla komunikat "signal: SIGPIPE" lub podobny, więc aby usunąć ten komunikat, trzeba samodzielnie obsłużyć SIGPIPE tak, jak chcemy, czyli po prostu cicho wyjść:

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

Wyświetlanie kontekstu komunikatu

Często chcemy zobaczyć kontekst, w jakim wystąpił błąd (na przykład, które zapytanie spowodowało panikę lub jakie problemy towarzyszące były widoczne przed awarią), a w tym grep służą opcje -A, -B i -C, które pokazują określoną liczbę wierszy po, przed i wokół komunikatu odpowiednio.

Niestety, nie znalazłem prostego sposobu na zrobienie tego samego w ClickHouse, dlatego aby wyświetlić kontekst, do każdego wiersza wyników wysyłane jest dodatkowe zapytanie w mniej więcej następującym stylu (szczegóły zależą od sortowania i tego, czy wyświetlany jest kontekst przed czy po):

SELECT time,millis,review_body FROM amazon
WHERE (time = 'CZAS_ZDARZENIA' AND millis < MILLISEKUNDY_ZDARZENIA) OR (time < 'CZAS_ZDARZENIA')
ORDER BY time DESC, millis DESC
LIMIT ILOŚĆ_WIERSZY_KONTEXTU
SETTINGS max_threads=1

Ponieważ zapytanie jest wysyłane prawie natychmiast po tym, jak ClickHouse zwrócił odpowiadający wiersz, trafia do pamięci podręcznej i ogólnie zapytanie jest wykonywane dość szybko, zużywając trochę CPU (zwykle zapytanie zajmuje około ~6 ms na mojej maszynie wirtualnej).

Wyświetlanie nowych wiadomości w czasie rzeczywistym

Aby wyświetlać przychodzące wiadomości w trybie (prawie) rzeczywistym, po prostu wykonujemy zapytanie co kilka sekund, zapamiętując ostatni znacznik czasu, który napotkaliśmy wcześniej.

Przykłady poleceń

Jak wyglądają typowe polecenia logscli w praktyce?

Jeśli załadowałeś zbiór danych Amazon, o którym wspomniałem na początku artykułu, będziesz mógł wykonać 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

Linki

Kod narzędzia (bez dokumentacji) jest dostępny na githubie pod adresem https://github.com/YuriyNasretdinov/logscli. Chętnie usłyszę Twoje myśli na temat mojego pomysłu na interfejs konsolowy do przeglądania logów oparty na ClickHouse.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster