Stiamo sviluppando l'interfaccia più comoda al mondo* per visualizzare i log

Stiamo sviluppando l'interfaccia più comoda al mondo* per visualizzare i log Se hai mai utilizzato le interfacce web per visualizzare i log, probabilmente avrai notato come, di norma, queste interfacce siano macchinose e (spesso) poco comode e reattive. Ad alcuni ci si può abituare, ad altri sono assolutamente terribili, ma mi sembra che la ragione di tutti i problemi sia che affrontiamo il compito di visualizzare i log in modo errato: proviamo a creare un'interfaccia web in cui la CLI (interfaccia a riga di comando) funziona meglio. Personalmente mi trovo molto a mio agio nel lavorare con tail, grep, awk e altri, e quindi per me l'interfaccia ideale per lavorare con i log sarebbe qualcosa di simile a tail e grep, ma che potrebbe anche essere usata per leggere i log che provengono da molti server. Ovviamente leggili da ClickHouse!

*secondo l'opinione personale dell'utente habra Sei forte

Incontra logscli

Non ho trovato un nome per la mia interfaccia e, a dire il vero, esiste piuttosto sotto forma di prototipo, ma se vuoi vedere subito il codice sorgente, allora sei il benvenuto: https://github.com/YuriyNasretdinov/logscli (350 righe di codice Go selezionato).

opportunità

Il mio obiettivo era creare un'interfaccia che sembrasse familiare a coloro che sono abituati a tail/grep, ovvero che supporti le seguenti cose:

  1. Visualizza tutti i registri, senza filtrare.
  2. Lascia le righe contenenti una sottostringa fissa (flag -F у grep).
  3. Lasciare le righe che corrispondono all'espressione regolare (flag -E у grep).
  4. Per impostazione predefinita, la visualizzazione avviene in ordine cronologico inverso, poiché in genere interessano per primi i registri più recenti.
  5. Mostra il contesto accanto a ciascuna riga (opzioni -A, -B и -C у grep, stampando N righe rispettivamente prima, dopo e attorno a ciascuna riga corrispondente).
  6. Visualizza i registri in arrivo in tempo reale, con o senza filtraggio (essenzialmente tail -f | grep).
  7. L'interfaccia deve essere compatibile con less, head, tail e altri: per impostazione predefinita, i risultati dovrebbero essere restituiti senza restrizioni sul loro numero; le righe vengono stampate come un flusso finché l'utente è interessato a riceverle; segnale SIGPIPE dovrebbero interrompere silenziosamente lo streaming dei log, proprio come fanno loro tail, grep e altre utilità UNIX.

implementazione

Presumo che tu sappia già in qualche modo come fornire i log a ClickHouse. In caso contrario, consiglio di provarlo lsd и gattinoe questo articolo sulla consegna dei log.

Per prima cosa devi decidere lo schema di base. Poiché di solito si desidera ricevere i registri ordinati in base all'ora, sembra logico archiviarli in questo modo. Se ci sono molte categorie di log e sono tutte dello stesso tipo, puoi creare una categoria di log come prima colonna della chiave primaria: questo ti consentirà di avere una tabella invece di diverse, il che sarà un grande vantaggio quando inserendo in ClickHouse (sui server con dischi rigidi, si consiglia di inserire i dati non più di ~ 1 volta al secondo per l'intero server).

Cioè, abbiamo bisogno approssimativamente del seguente schema di tabella:

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

Sfortunatamente, non sono riuscito a trovare immediatamente nessun open source con log realistici da poter prendere e scaricare, quindi ho preso questo come esempio recensioni di prodotti Amazon prima del 2015. Naturalmente la loro struttura non è esattamente la stessa dei log di testo, ma a scopo illustrativo questo non è importante.

istruzioni per caricare recensioni Amazon su ClickHouse

Creiamo una tabella:

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

Nel set di dati di Amazon c'è solo una data per una revisione, ma non c'è un'ora esatta, quindi integriamo questi dati con un randon.

Non è necessario scaricare tutti i file tsv e limitarsi ai primi ~10-20 per ottenere un set di dati abbastanza ampio che non entrerà in 16 GB di RAM. Per caricare i file TSV ho utilizzato il seguente comando:

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

Su un disco permanente standard (che è un HDD) in Google Cloud con una dimensione di 1000 GB (ho preso questa dimensione principalmente in modo che la velocità fosse un po' più alta, anche se forse un SSD della dimensione richiesta sarebbe stato più economico) il caricamento la velocità era di circa ~ 75 MB/sec su 4 core.

  • Devo effettuare una prenotazione perché lavoro in Google, ma ho utilizzato un account personale e questo articolo non ha nulla a che fare con il mio lavoro in azienda

Produrrò tutte le illustrazioni con questo particolare set di dati, poiché è tutto ciò che avevo a portata di mano.

Mostra l'avanzamento della scansione dei dati

Dato che in ClickHouse utilizzeremo una scansione completa su una tabella con log, e questa operazione può richiedere molto tempo e potrebbe non produrre risultati per molto tempo se vengono trovate poche corrispondenze, è consigliabile poter mostrare il avanzamento della query fino alla ricezione delle prime righe con il risultato. Per fare ciò, c'è un parametro nell'interfaccia HTTP che ti consente di inviare l'avanzamento nelle intestazioni HTTP: send_progress_in_http_headers=1. Sfortunatamente, la libreria Go standard non può leggere le intestazioni man mano che vengono ricevute, ma l'interfaccia HTTP 1.0 (da non confondere con 1.1!) è supportata da ClickHouse, quindi puoi aprire una connessione TCP grezza a ClickHouse e inviarla lì GET /?query=... HTTP/1.0nn e ricevere le intestazioni e il corpo della risposta senza alcun escape o crittografia, quindi in questo caso non abbiamo nemmeno bisogno di utilizzare la libreria standard.

Log in streaming da ClickHouse

ClickHouse dispone di un'ottimizzazione per le query con ORDER BY da un tempo relativamente lungo (dal 2019?), quindi una query come

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

Inizierà immediatamente a restituire le righe che contengono la sottostringa "qualcosa" nel messaggio, senza attendere il completamento della scansione.

Inoltre, sarebbe molto conveniente se ClickHouse stesso annullasse la richiesta quando la connessione ad esso veniva chiusa, ma questo non è il comportamento predefinito. La cancellazione automatica della richiesta può essere abilitata utilizzando l'opzione cancel_http_readonly_queries_on_client_close=1.

Gestione corretta di SIGPIPE in Go

Quando esegui, ad esempio, il comando some_cmd | head -n 10, esattamente come il comando some_cmd interrompe l'esecuzione quando head sottratto 10 righe? La risposta è semplice: quando head termina, la pipe si chiude e lo stdout del comando some_cmd inizia a puntare, condizionatamente, "verso il nulla". Quando some_cmd tenta di scrivere su una pipe chiusa, riceve un segnale SIGPIPE, che termina silenziosamente il programma per impostazione predefinita.

In Go questo accade anche per impostazione predefinita, ma il gestore del segnale SIGPIPE stampa anche "signal: SIGPIPE" o un messaggio simile alla fine, e per cancellare questo messaggio dobbiamo solo gestire noi stessi SIGPIPE come vogliamo, cioè in silenzio Uscita:

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

Mostra il contesto del messaggio

Spesso si desidera vedere il contesto in cui si è verificato un errore (ad esempio, quale richiesta ha causato un panico o quali problemi correlati erano visibili prima dell'arresto anomalo) e in grep Questo viene fatto utilizzando le opzioni -A, -B e -C, che mostrano rispettivamente il numero specificato di righe dopo, prima e attorno al messaggio.

Sfortunatamente, non ho trovato un modo semplice per fare lo stesso in ClickHouse, quindi per visualizzare il contesto, viene inviata una richiesta aggiuntiva come questa a ciascuna riga del risultato (i dettagli dipendono dall'ordinamento e se il contesto viene mostrato prima o dopo):

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

Poiché la richiesta viene inviata quasi immediatamente dopo che ClickHouse restituisce la riga corrispondente, finisce nella cache e in generale la richiesta viene eseguita abbastanza rapidamente e consuma un po' di CPU (di solito la richiesta dura circa ~6 ms sulla mia macchina virtuale).

Mostra nuovi messaggi in tempo reale

Per mostrare i messaggi in arrivo in (quasi) tempo reale, eseguiamo semplicemente la richiesta una volta ogni pochi secondi, ricordando l'ultimo timestamp che abbiamo incontrato prima.

Esempi di comandi

Come sono in pratica i tipici comandi logscli?

Se hai scaricato il set di dati Amazon di cui ho parlato all'inizio dell'articolo, puoi eseguire i seguenti comandi:

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

riferimenti

Il codice dell'utilità (senza documentazione) è disponibile su github all'indirizzo https://github.com/YuriyNasretdinov/logscli. Sarei felice di sentire le tue opinioni sulla mia idea di un'interfaccia console per la visualizzazione dei registri basata su ClickHouse.

Fonte: habr.com

Aggiungi un commento