Falls Sie jemals Webinterfaces zur Ansicht von Logs verwendet haben, ist Ihnen sicherlich aufgefallen, wie oft diese Interfaces umständlich und (häufig) nicht besonders benutzerfreundlich oder reaktionsschnell sind. An einige kann man sich gewöhnen, während andere äußerst unpraktisch sind. Meiner Meinung nach liegt das Hauptproblem darin, dass wir die Herausforderung der Log-Analyse nicht richtig angehen: Wir versuchen, ein Webinterface zu schaffen, wo ein CLI (Command Line Interface) besser geeignet wäre. Ich persönlich arbeite sehr gerne mit Tools wie tail, grep und awk, und daher wäre ein ideales Interface für die Log-Analyse für mich etwas, das wie tail und grep funktioniert, jedoch die Möglichkeit bietet, Logs von zahlreichen Servern zu lesen. Das bedeutet natürlich, dass ich sie aus ClickHouse lesen möchte.
*nach persönlicher Meinung eines Habr-Nutzers
Willkommen bei logscli
Ich habe meinem Interface keinen Namen gegeben, und um ehrlich zu sein, existiert es eher in Form eines Prototyps. Doch wenn Sie gleich den Quellcode ansehen wollen, sind Sie herzlich eingeladen: (350 Zeilen feiner Go-Codes).
Funktionen
Ich hatte das Ziel, eine Benutzeroberfläche zu schaffen, die denjenigen vertraut vorkommt, die mit tail/grep vertraut sind, d.h. folgende Funktionen zu unterstützen:
- Anzeigen aller Logs ohne Filterung.
- Behalten von Zeilen, die eine feste Teilzeichenfolge enthalten (Flagge
-Fugrep). - Behalten von Zeilen, die einem regulären Ausdruck entsprechen (Flagge
-Eugrep). - Standardmäßig erfolgt die Anzeige in umgekehrter chronologischer Reihenfolge, da in der Regel am meisten Interesse an den neuesten Logs besteht.
- Kontextanzeige um jede Zeile herum (Parameter
-A,-Bund-Cugrep, die N Zeilen vor, nach und um jede Übereinstimmung herum drucken). - Anzeigen der eingehenden Logs in Echtzeit, mit und ohne Filterung (im Grunde
tail -f | grep). - Die Benutzeroberfläche sollte kompatibel sein mit
less,head,tailund anderen – standardmäßig sollten die Ergebnisse ohne Einschränkungen zurückgegeben werden; die Zeilen werden fortlaufend ausgegeben, solange der Benutzer an deren Empfang interessiert ist; das SignalSIGPIPEsollte das Streaming der Logs stillschweigend unterbrechen, genau wie es auchtail,grepund andere UNIX-Tools tun.
Implementierung
Ich gehe davon aus, dass Sie bereits auf irgendeine Weise in der Lage sind, Logs an ClickHouse zu liefern. Falls nicht, empfehle ich, es mit und , sowie .
Zuerst müssen wir uns über das Datenbankschema im Klaren sein. Da Logs in der Regel zeitlich sortiert sein sollen, macht es Sinn, sie auch so zu speichern. Wenn es viele Log-Kategorien gibt und sie alle ähnlich sind, kann die Log-Kategorie als erste Spalte des Primärschlüssels verwendet werden – das ermöglicht es, eine einzige Tabelle anstelle mehrerer zu haben, was bei der Einfügung in ClickHouse von Vorteil ist (bei Servern mit Festplatten wird empfohlen, Daten nicht häufiger als etwa einmal pro Sekunde einzufügen). für den gesamten Server).
Das heißt, wir benötigen ungefähr folgendes Tabellenschema:
CREATE TABLE logs(
category LowCardinality(String), -- Log-Kategorie (optional)
time DateTime, -- Zeitpunkt des Ereignisses
millis UInt16, -- Millisekunden (es können auch Mikrosekunden usw. sein): wird empfohlen, wenn viele Ereignisse vorliegen, um diese besser voneinander unterscheiden zu können
..., -- Ihre eigenen Felder, wie z.B. Servername, Log-Level usw.
message String -- Nachrichteninhalt
) ENGINE=MergeTree()
ORDER BY (category, time, millis)Leider konnte ich sofort keine offenen Quellen mit realistischen Logs finden, die heruntergeladen werden konnten, daher habe ich stattdessen als Beispiel . Ihre Struktur ist sicherlich nicht ganz die gleiche wie die von Text-Logs, aber für die Veranschaulichung ist das nicht entscheidend.
Anleitung zum Hochladen von Amazon-Bewertungen in ClickHouse
Wir erstellen eine Tabelle:
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=8192Im Amazon-Datensatz gibt es nur das Datum für die Bewertung, aber keine genaue Uhrzeit, daher füllen wir diese Daten mit Zufallswerten.
Es ist nicht notwendig, alle TSV-Dateien herunterzuladen und es genügt, sich auf die ersten ~10-20 zu beschränken, um bereits einen ausreichend großen Datensatz zu erhalten, der nicht in 16 GB RAM hineinpasst. Für das Hochladen der TSV-Dateien habe ich den folgenden Befehl verwendet:
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'
doneAuf einer standardmäßigen Persistent Disk (HDD) in Google Cloud mit einer Größe von 1000 GB (diesen Wert habe ich gewählt, um die Geschwindigkeit etwas zu erhöhen, obwohl eine SSD in der benötigten Größe möglicherweise günstiger gewesen wäre) lag die Uploadgeschwindigkeit bei etwa ~75 MB/s auf 4 Kernen.
- Ich muss erwähnen, dass ich bei Google arbeite, aber ich habe mein persönliches Konto genutzt und dieser Artikel hat nichts mit meiner Arbeit im Unternehmen zu tun.
Alle Illustrationen werde ich genau mit diesem Datensatz erstellen, da dies alles ist, was ich zur Verfügung hatte.
Fortschrittsanzeige beim Scannen von Daten
Da wir in ClickHouse einen vollständigen Scan der Logtabelle durchführen werden und diese Operation erhebliche Zeit in Anspruch nehmen kann sowie lange keine Ergebnisse liefern kann, wenn wenige Übereinstimmungen gefunden werden, ist es wünschenswert, den Fortschritt der Anfrage bis zu den ersten Ergebnissen anzuzeigen. Dafür gibt es im HTTP-Interface einen Parameter, der es ermöglicht, den Fortschritt in den HTTP-Headern zurückzugeben: send_progress_in_http_headers=1. Leider kann die Standardbibliothek von Go keine Header beim Empfang lesen, aber das HTTP 1.0-Interface (nicht zu verwechseln mit 1.1!) wird von ClickHouse unterstützt. Daher kann eine rohe TCP-Verbindung zu ClickHouse geöffnet werden, um GET /?query=... HTTP/1.0nn und die Header sowie den Antwortkörper ohne jegliche Escape- und Verschlüsselung zu erhalten, sodass in diesem Fall die Standardbibliothek sogar nicht benötigt wird.
Streaming von Protokollen aus ClickHouse
ClickHouse hat bereits seit einiger Zeit (seit 2019?) eine Optimierung für Anfragen mit ORDER BY, sodass eine Anfrage wie
SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESCsofort die Zeilen zurückgibt, bei denen in der Nachricht die Teilzeichenfolge "something" enthalten ist, ohne auf das Ende des Scannens zu warten.
Es wäre auch sehr praktisch, wenn ClickHouse die Anfrage automatisch abbrechen würde, wenn die Verbindung geschlossen wird, was jedoch nicht das Standardverhalten ist. Das automatische Abbrechen von Anfragen kann durch die Option cancel_http_readonly_queries_on_client_close=1.
Korrekte Behandlung von SIGPIPE in Go
Wenn Sie beispielsweise den Befehl some_cmd | head -n 10ausführen, wie genau der Befehl some_cmd beendet wird, wenn head Hast du 10 Zeilen ausgelesen? Die Antwort ist einfach: wenn head abgeschlossen ist, wird das Pipe geschlossen, und stdout des Befehls some_cmd zeigt dann, sagen wir, ins Leere. Wenn some_cmd versucht, in ein geschlossenes Pipe zu schreiben, .
In Go passiert das standardmäßig ebenfalls, aber der SIGPIPE-Signal-Handler gibt am Ende auch die Meldung "signal: SIGPIPE" oder eine ähnliche Nachricht aus. Um diese Nachricht zu unterdrücken, müssen wir SIGPIPE entsprechend verarbeiten, also einfach still beenden:
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGPIPE)
go func() {
<-ch
os.Exit(0)
}()Kontext der Nachricht anzeigen
Oft möchte man den Kontext sehen, in dem ein Fehler aufgetreten ist (zum Beispiel, welcher Request die Panik ausgelöst hat oder welche begleitenden Probleme vor dem Absturz sichtbar waren), und dafür grep dienen die Optionen -A, -B und -C, die die angegebene Anzahl von Zeilen nach, vor und um die Nachricht herum anzeigen.
Leider habe ich keinen einfachen Weg gefunden, dasselbe in ClickHouse zu tun. Daher wird für jede Zeile des Ergebnisses eine zusätzliche Anfrage gesendet, die ungefähr folgendes aussieht (die Details hängen von der Sortierung ab und davon, ob der Kontext davor oder danach angezeigt wird):
SELECT time, millis, review_body FROM amazon
WHERE (time = 'EREIGNISZEIT' AND millis < EREIGNIS_MILLIS) OR (time < 'EREIGNISZEIT')
ORDER BY time DESC, millis DESC
LIMIT ANZAHL_KONTEXTZEILEN
SETTINGS max_threads=1Da die Anfrage fast sofort gesendet wird, nachdem ClickHouse die entsprechende Zeile zurückgegeben hat, wird sie im Cache gespeichert und die Anfrage wird insgesamt recht schnell ausgeführt, wobei nur wenig CPU verbraucht wird (in der Regel dauert die Anfrage etwa ~6 ms auf meiner virtuellen Maschine).
Anzeigen neuer Nachrichten in Echtzeit
Um eingehende Nachrichten in (fast) Echtzeit anzuzeigen, führen wir die Anfrage einfach alle paar Sekunden aus und merken uns den letzten Zeitstempel, den wir zuvor getroffen haben.
Beispiele für Befehle
Wie sehen typische logscli-Befehle in der Praxis aus?
Wenn Sie das Amazon-Dataset hochgeladen haben, das ich zu Beginn des Artikels erwähnt habe, können Sie die folgenden Befehle ausführen:
# Показать строки, где встречается слово 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' -tailfLinks
Der Tool-Code (ohne Dokumentation) ist auf GitHub verfügbar unter . Ich freue mich auf Ihr Feedback zu meiner Idee für ein konsolenbasiertes Interface zur Ansicht von Protokollen auf Basis von ClickHouse.
Quelle: habr.com
