Se hai mai utilizzato interfacce web per visualizzare i log, probabilmente hai notato quanto spesso queste interfacce siano ingombranti e (spesso) poco intuitive e reattive. Alcune si possono apprezzare, altre sono davvero terribili, ma penso che la causa principale di tutti questi problemi risieda nel fatto che ci approcciamo male al compito di visualizzare i log: cerchiamo di creare un'interfaccia web dove il CLI (interfaccia a riga di comando) funziona meglio. Personalmente, mi trovo molto a mio agio a lavorare con tail, grep, awk e simili, e quindi un'interfaccia ideale per lavorare con i log sarebbe qualcosa di simile a tail e grep, ma utilizzabile per leggere i log provenienti da molti server. In altre parole, leggerli da ClickHouse!
*a parere personale di un utente di Habr
Presentiamo logscli
Non ho trovato un nome per la mia interfaccia, e a dire il vero, esiste più come prototipo, ma se vuoi dare un'occhiata al codice sorgente, sei il benvenuto: (350 righe di codice selezionato in Go).
Capacità
Ho voluto creare un'interfaccia che sembrasse familiare a chi è abituato a tail/grep, supportando le seguenti funzionalità:
- Visualizzazione di tutti i log, senza alcun filtro.
- Lasciare le righe che contengono una sottostringa fissa (flag
-Fagrep). - Lasciare le righe che corrispondono a un'espressione regolare (flag
-Eagrep). - Per default, la visualizzazione avviene in ordine cronologico inverso, poiché di solito ci interessano prima i log più recenti.
- Mostrare il contesto attorno a ogni riga (parametri
-A,-Be-Cagrep, che stampano N righe prima, dopo e attorno a ciascuna riga corrispondente, rispettivamente). - Visualizzazione dei log in tempo reale, con o senza filtro (essenzialmente
tail -f | grep). - L'interfaccia deve essere compatibile con
less,head,taile altri — per default devono essere restituiti risultati senza limiti sul numero; le righe vengono stampate in streaming finché l'utente è interessato a riceverle; il segnaleSIGPIPEdovrebbe interrompere silenziosamente lo streaming dei log, proprio come fannotail,grepe altre utility UNIX.
Implementazione
Supporrò che già sappiate come consegnare i log a ClickHouse. Se non lo sapete, vi consiglio di provare e , così come .
Per iniziare, è necessario determinare lo schema del database. Poiché di solito si desidera ricevere i log ordinati per tempo, è logico memorizzarli in questo modo. Se ci sono molte categorie di log e sono tutte simili, si può utilizzarne una come prima colonna della chiave primaria — questo permetterà di avere una sola tabella invece di diverse, il che sarà un grande vantaggio quando si inseriscono dati in ClickHouse (nei server con dischi rigidi si consiglia di non inserire dati più di una volta ogni ~1 secondo) per l'intero server).
Quindi, abbiamo bisogno di uno schema di tabelle simile al seguente:
CREATE TABLE logs(
category LowCardinality(String), -- categoria dei log (opzionale)
time DateTime, -- tempo dell'evento
millis UInt16, -- millisecondi (possono esserci anche microsecondi, ecc.): si consiglia di conservarli, se ci sono molti eventi, per facilitare la distinzione tra di essi
..., -- i tuoi campi personalizzati, come nome del server, livello di registrazione, e così via
message String -- testo del messaggio
) ENGINE=MergeTree()
ORDER BY (category, time, millis)Sfortunatamente, non sono riuscito a trovare fonti aperte con log realistici da scaricare, quindi ho preso invece per esempio . Certamente, la loro struttura non è esattamente la stessa di quella dei log testuali, ma per illustrazione non è fondamentale.
istruzioni per il caricamento delle recensioni Amazon in 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=8192Nel dataset di Amazon c'è solo la data per la recensione, ma non l'ora esatta, quindi riempiremo questi dati con valori casuali.
Non è necessario scaricare tutti i file tsv e ci si può limitare ai primi ~10-20, per avere già un set di dati sufficientemente ampio che non entra in 16 GB di memoria RAM. Per il caricamento dei file TSV ho utilizzato il seguente comando:
per 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'
doneSu un disco persistente standard (HDD) da 1000 GB in Google Cloud (questa dimensione l'ho scelta principalmente per avere una velocità leggermente superiore, anche se probabilmente un SSD della dimensione desiderata sarebbe costato meno) la velocità di caricamento è stata di circa ~75 MB/s su 4 core.
- Devo precisare che lavoro in Google, ma ho utilizzato un account personale e questo articolo non ha nulla a che fare con il mio lavoro in azienda.
Tutte le illustrazioni le realizzerò utilizzando proprio questo dataset, poiché è tutto ciò che avevo a disposizione.
Mostra il progresso della scansione dei dati
Poiché in ClickHouse utilizzeremo una scansione completa sulla tabella dei log e questa operazione potrebbe richiedere un tempo considerevole e non restituire rapidamente alcun risultato se le corrispondenze trovate sono poche, è preferibile essere in grado di mostrare il progresso dell'esecuzione della query prima di ricevere le prime righe con il risultato. A tal fine, nell'interfaccia HTTP è presente un parametro che consente di restituire il progresso nelle intestazioni HTTP: send_progress_in_http_headers=1. Purtroppo, la libreria standard di Go non è in grado di leggere gli header mentre vengono ricevuti, ma l'interfaccia HTTP 1.0 (non confondetela con la 1.1!) è supportata da ClickHouse, quindi è possibile aprire una connessione TCP grezza con ClickHouse e inviare GET /?query=... HTTP/1.0nn e ricevere in risposta gli header e il corpo della risposta senza alcuna codifica o crittografia, quindi in questo caso non è neanche necessario utilizzare la libreria standard.
Streaming dei log da ClickHouse
In ClickHouse esiste già da un po' (dal 2019?) un'ottimizzazione per le query con ORDER BY, quindi una query del tipo
SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESCinizierà subito a restituire le righe in cui il messaggio contiene la sottostringa "something", senza attendere il termine della scansione.
Inoltre, sarebbe molto comodo se ClickHouse annullasse automaticamente la query quando la connessione viene chiusa, ma questo non è il comportamento predefinito. L'annullamento automatico della query può essere attivato con l'opzione cancel_http_readonly_queries_on_client_close=1.
Corretto trattamento di SIGPIPE in Go
Quando si esegue, ad esempio, il comando some_cmd | head -n 10, in che modo il comando some_cmd interrompe la sua esecuzione quando head Hai letto 10 righe? La risposta è semplice: quando head si conclude, il pipe si chiude e stdout del comando some_cmd inizia a puntare, in modo figurato, a «nulla». Quando some_cmd cerca di scrivere in un pipe chiuso, .
In Go, per impostazione predefinita, succede anche questo, ma il gestore del segnale SIGPIPE alla fine stampa anche "signal: SIGPIPE" o un messaggio simile, e per rimuovere questo messaggio è necessario gestire SIGPIPE come desideriamo, cioè semplicemente uscire silenziosamente:
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 è avvenuto un errore (ad esempio, quale richiesta ha causato il panic, o quali problemi collaterali erano visibili prima del crash), e in grep questo servono le opzioni -A, -B e -C, che mostrano il numero specificato di righe dopo, prima e attorno al messaggio rispettivamente.
Sfortunatamente, non ho trovato un modo semplice per fare la stessa cosa in ClickHouse, quindi, per visualizzare il contesto, viene inviata una richiesta aggiuntiva per ogni riga del risultato, simile a questa (i dettagli dipendono dall'ordinamento e se il contesto è mostrato prima o dopo):
SELECT time,millis,review_body FROM amazon
WHERE (time = 'TEMPO_EVENTO' AND millis < MILLISECOND_EVENTO) OR (time < 'TEMPO_EVENTO')
ORDER BY time DESC, millis DESC
LIMIT NUMERO_RIGHE_CONTEXTO
SETTINGS max_threads=1Poiché la richiesta viene inviata quasi subito dopo che ClickHouse ha restituito la riga corrispondente, essa viene memorizzata nella cache e in generale la richiesta viene eseguita abbastanza velocemente e consuma un po' di CPU (di solito la richiesta richiede circa ~6 ms sulla mia macchina virtuale).
Visualizzazione di nuovi messaggi in tempo reale
Per mostrare i messaggi in arrivo in tempo (quasi) reale, basta eseguire la richiesta ogni pochi secondi, ricordando l'ultimo timestamp che abbiamo incontrato prima.
Esempi di comandi
Quali sono i comandi logscli tipici in pratica?
Se hai caricato il dataset Amazon che ho menzionato 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' -tailfLink
Il codice dell'utilità (senza documentazione) è disponibile su github all'indirizzo . Sarei felice di conoscere le vostre opinioni riguardo alla mia idea per un'interfaccia console per la visualizzazione dei log basata su ClickHouse.
Fonte: habr.com
