Stiamo sviluppando l'interfaccia per la visualizzazione dei log più comoda al mondo*

Stiamo sviluppando l'interfaccia per la visualizzazione dei log più comoda al mondo* Se hai mai utilizzato interfacce web per visualizzare log, avrai sicuramente notato quanto, in generale, queste interfacce siano ingombranti e (spesso) non troppo comode e reattive. Alcune si possono abituare, altre sono davvero terribili, ma, a mio avviso, la causa di tutti i problemi è che ci approcciamo in modo errato alla questione della visualizzazione dei log: cerchiamo di creare un'interfaccia web dove funziona meglio la CLI (interfaccia della riga di comando). Personalmente mi trovo molto a mio agio a lavorare con tail, grep, awk e simili, e quindi la mia interfaccia ideale per lavorare con i log sarebbe qualcosa di simile a tail e grep, ma che potesse essere utilizzato per leggere i log provenienti da molti server. Cioè, ovviamente, leggerli da ClickHouse!

*secondo l'opinione personale di un utente di Habr youROCK

Dai il benvenuto a 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 subito al codice sorgente, sei il benvenuto: https://github.com/YuriyNasretdinov/logscli (350 righe di codice selezionato in Go).

Funzionalità

Mi sono posto l'obiettivo di creare un'interfaccia che sembrasse familiare a chi è abituato a tail/grep, supportando quindi le seguenti cose:

  1. Visualizzazione di tutti i log, senza filtraggio.
  2. Conservare le righe contenenti una sottostringa fissa (flag -F a grep).
  3. Conservare le righe che corrispondono a una espressione regolare (flag -E a grep).
  4. Per impostazione predefinita, la visualizzazione avviene in ordine cronologico inverso, poiché di solito si è interessati prima ai log più recenti.
  5. Mostrare il contesto accanto a ogni riga (parametri -A, -B e -C a grep, che stampano N righe prima, dopo e intorno a ciascuna riga corrispondente).
  6. Visualizzazione dei log in tempo reale, con filtraggio e senza (in sostanza tail -f | grep).
  7. L'interfaccia deve essere compatibile con less, head, tail e altri — per impostazione predefinita i risultati dovrebbero essere restituiti senza limitazioni sul numero; le righe vengono stampate in streaming finché l'utente è interessato a riceverle; il segnale SIGPIPE deve interrompere silenziosamente lo streaming dei log, proprio come fanno tail, grep e altre utility UNIX.

Implementazione

Presumerò che tu già sappia in qualche modo come inviare i log a ClickHouse. Se no, ti consiglio di provare lsd e kittenhouse, e inoltre questo articolo sulla consegna dei log.

Per iniziare, è necessario definire lo schema del database. Poiché solitamente si desidera ricevere i log ordinati per tempo, sembra logico conservarli in questo modo. Se ci sono molte categorie di log e sono tutte simili, si può utilizzare la categoria del log come prima colonna della chiave primaria; questo permetterà di avere una sola tabella invece di più, il che sarà un grande vantaggio durante l'inserimento in ClickHouse (sui server con dischi rigidi, è consigliato inserire i dati non più spesso di ~1 volta al secondo su tutto il server).

Cioè, abbiamo bisogno di uno schema di tabelle approssimativamente simile a questo:

CREATE TABLE logs(
    category LowCardinality(String), -- categoria dei log (opzionale)
    time DateTime, -- tempo dell'evento
    millis UInt16, -- millisecondi (possono essere microsecondi, ecc.): si consiglia di memorizzare, se ci sono molti eventi, per facilitare la distinzione tra gli eventi
    ..., -- i tuoi campi personali, come nome del server, livello di log, e così via
    message String -- testo del messaggio
) ENGINE=MergeTree()
ORDER BY (category, time, millis)

Sfortunatamente, non sono riuscito a trovare immediatamente fonti aperte con log realistici che potessero essere scaricati, quindi ho utilizzato invece per l'esempio recensioni sui prodotti di Amazon fino al 2015. Certamente, la loro struttura non è esattamente la stessa dei log testuali, ma per scopi illustrativi 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=8192

Nel dataset di Amazon è presente solo la data della 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 ottenere già un insieme di dati sufficientemente grande che non possa essere caricato in 16 GB di RAM. Per il caricamento dei 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 Persistent Disk standard (HDD) di Google Cloud con una dimensione di 1000 GB (ho scelto questa dimensione principalmente per avere una velocità leggermente superiore, anche se probabilmente un SSD della giusta capacità sarebbe costato meno), la velocità di caricamento è stata di circa ~75 MB/sec su 4 core.

  • Devo precisare che lavoro in Google, ma ho utilizzato un account personale e questo articolo non ha alcun legame con il mio lavoro in azienda.

Tutte le illustrazioni le produrrò 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 della tabella dei log, e questa operazione può richiedere un tempo significativo e non restituire risultati per lungo tempo se ci sono pochi risultati corrispondenti, è preferibile essere in grado di mostrare il progresso dell'esecuzione della query fino a che non si ottengono le prime righe con i risultati. Per fare ciò, nell'interfaccia HTTP è presente un parametro che consente di restituire il progresso nelle intestazioni HTTP: send_progress_in_http_headers=1. Sfortunatamente, la libreria standard di Go non è in grado di leggere le intestazioni man mano che vengono ricevute, ma l'interfaccia HTTP 1.0 (non confondetela con la 1.1!) è supportata da ClickHouse, quindi è possibile aprire una connessione TCP grezza con ClickHouse, inviare GET \/?query=... HTTP\/1.0nn e ricevere in risposta intestazioni e corpo della risposta senza alcun tipo di escaping o crittografia, per cui in questo caso non è nemmeno 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 DESC

inizierà immediatamente a restituire le righe in cui il messaggio contiene la sottostringa "something", senza dover attendere il completamento della scansione.

Sarebbe anche 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 abilitato con l'opzione cancel_http_readonly_queries_on_client_close=1.

Gestione corretta di SIGPIPE in Go

Quando eseguite, ad esempio, il comando some_cmd | head -n 10, in che modo il comando some_cmd termina la sua esecuzione quando head ha letto 10 righe? La risposta è semplice: quando head si completa, la pipe si chiude, e stdout del comando some_cmd inizia a puntare, in un certo senso, «nello spazio». Quando some_cmd cerca di scrivere in una pipe chiusa, riceve un segnale SIGPIPE, che per impostazione predefinita termina silenziosamente il programma..

In Go, questo avviene automaticamente, 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 vogliamo, cioè semplicemente uscire silenziosamente:

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

Visualizzazione del contesto del messaggio

Spesso è utile vedere il contesto in cui si è verificato un errore (ad esempio, quale richiesta ha causato il crash, o quali problemi concomitanti erano visibili prima del fallimento), e grep a questo servono le opzioni -A, -B e -C, che mostrano il numero specificato di righe dopo, prima e attorno al messaggio, rispettivamente.

Sfortunatamente, non sono riuscito a trovare un modo semplice per fare lo stesso in ClickHouse, quindi, per visualizzare il contesto, a ciascuna riga del risultato viene inviato una richiesta aggiuntiva di tipo simile (i dettagli dipendono dall'ordinamento e se il contesto viene mostrato prima o dopo):

SELECT time,millis,review_body FROM amazon
WHERE (time = 'TEMPO_EVENTO' AND millis < MILLISECONDI_EVENTO) OR (time < 'TEMPO_EVENTO')
ORDER BY time DESC, millis DESC
LIMIT NUMERO_RIGHE_CONTEXTE
SETTINGS max_threads=1

Poiché 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 rapidamente 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

Come appaiono nella pratica i comandi tipici di logscli?

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' -tailf

Link

Il codice dell'utilità (senza documentazione) è disponibile su github all'indirizzo https://github.com/YuriyNasretdinov/logscli. Sarò felice di conoscere le tue opinioni riguardo alla mia idea di interfaccia a riga di comando per visualizzare i log basata su ClickHouse.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster