Αναπτύσσουμε την πιο βολική διεπαφή στον κόσμο* για την προβολή αρχείων καταγραφής

Αναπτύσσουμε την πιο βολική διεπαφή στον κόσμο* για την προβολή αρχείων καταγραφής Εάν έχετε χρησιμοποιήσει ποτέ διεπαφές ιστού για να προβάλετε αρχεία καταγραφής, τότε πιθανότατα έχετε παρατηρήσει πώς, κατά κανόνα, αυτές οι διεπαφές είναι δυσκίνητες και (συχνά) δεν είναι πολύ βολικές και ανταποκρίνονται. Μερικά μπορείτε να τα συνηθίσετε, μερικά είναι απολύτως τρομερά, αλλά μου φαίνεται ότι ο λόγος για όλα τα προβλήματα είναι ότι προσεγγίζουμε το έργο της εσφαλμένης προβολής αρχείων καταγραφής: προσπαθούμε να δημιουργήσουμε μια διεπαφή ιστού όπου το CLI (διεπαφή γραμμής εντολών) λειτουργεί καλύτερα. Προσωπικά αισθάνομαι πολύ άνετα να δουλεύω με tail, grep, awk και άλλα, και επομένως για μένα η ιδανική διεπαφή για εργασία με αρχεία καταγραφής θα ήταν κάτι παρόμοιο με το tail και το grep, αλλά το οποίο θα μπορούσε επίσης να χρησιμοποιηθεί για την ανάγνωση αρχείων καταγραφής που προέρχονται από πολλούς διακομιστές. Αυτό είναι, φυσικά, διαβάστε τα από το ClickHouse!

*σύμφωνα με την προσωπική γνώμη του χρήστη habra ο βράχος σου

Γνωρίστε το logscli

Δεν βρήκα όνομα για τη διεπαφή μου και, για να είμαι ειλικρινής, υπάρχει μάλλον με τη μορφή πρωτοτύπου, αλλά αν θέλετε να δείτε αμέσως τον πηγαίο κώδικα, τότε είστε ευπρόσδεκτοι: https://github.com/YuriyNasretdinov/logscli (350 γραμμές επιλεγμένου κώδικα Go).

Δυνατότητες

Ο στόχος μου ήταν να φτιάξω μια διεπαφή που θα φαινόταν οικεία σε όσους έχουν συνηθίσει στο tail/grep, δηλαδή να υποστηρίζει τα ακόλουθα πράγματα:

  1. Προβολή όλων των αρχείων καταγραφής, χωρίς φιλτράρισμα.
  2. Αφήστε τις γραμμές που περιέχουν μια σταθερή υποσυμβολοσειρά (σημαία -F у grep).
  3. Αφήστε τις γραμμές που ταιριάζουν με την κανονική έκφραση (σημαία -E у grep).
  4. Από προεπιλογή, η προβολή γίνεται με αντίστροφη χρονολογική σειρά, καθώς τα πιο πρόσφατα αρχεία καταγραφής είναι συνήθως ενδιαφέροντα πρώτα.
  5. Εμφάνιση περιβάλλοντος δίπλα σε κάθε γραμμή (επιλογές -A, -B и -C у grep, εκτύπωση Ν γραμμών πριν, μετά και γύρω από κάθε αντίστοιχη γραμμή, αντίστοιχα).
  6. Δείτε τα εισερχόμενα αρχεία καταγραφής σε πραγματικό χρόνο, με ή χωρίς φιλτράρισμα (ουσιαστικά tail -f | grep).
  7. Η διεπαφή πρέπει να είναι συμβατή με less, head, tail και άλλα - από προεπιλογή, τα αποτελέσματα θα πρέπει να επιστρέφονται χωρίς περιορισμούς στον αριθμό τους. Οι γραμμές εκτυπώνονται ως ροή, εφόσον ο χρήστης ενδιαφέρεται να τις λάβει. σήμα SIGPIPE θα πρέπει να διακόπτουν σιωπηλά τη ροή αρχείων καταγραφής, όπως ακριβώς κάνουν tail, grep και άλλα βοηθητικά προγράμματα UNIX.

Реализация

Θα υποθέσω ότι γνωρίζετε ήδη με κάποιο τρόπο πώς να παραδίδετε αρχεία καταγραφής στο ClickHouse. Αν όχι, προτείνω να το δοκιμάσετε LSD и γατάκιΚαι αυτό το άρθρο σχετικά με την παράδοση αρχείων καταγραφής.

Πρώτα πρέπει να αποφασίσετε για το βασικό σχήμα. Δεδομένου ότι συνήθως θέλετε να λαμβάνετε αρχεία καταγραφής ταξινομημένα κατά χρόνο, φαίνεται λογικό να τα αποθηκεύετε με αυτόν τον τρόπο. Εάν υπάρχουν πολλές κατηγορίες αρχείων καταγραφής και είναι όλες του ίδιου τύπου, τότε μπορείτε να δημιουργήσετε μια κατηγορία αρχείων καταγραφής ως την πρώτη στήλη του πρωτεύοντος κλειδιού - αυτό θα σας επιτρέψει να έχετε έναν πίνακα αντί για πολλούς, κάτι που θα είναι ένα μεγάλο πλεονέκτημα όταν εισαγωγή στο ClickHouse (σε διακομιστές με σκληρούς δίσκους, συνιστάται η εισαγωγή δεδομένων όχι περισσότερο από ~1 φορές ανά δευτερόλεπτο για ολόκληρο τον διακομιστή).

Δηλαδή, χρειαζόμαστε περίπου το ακόλουθο σχήμα πίνακα:

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

Δυστυχώς, δεν μπόρεσα να βρω αμέσως ανοιχτές πηγές με ρεαλιστικά αρχεία καταγραφής που θα μπορούσα να πιάσω και να κατεβάσω, γι' αυτό το πήρα ως παράδειγμα κριτικές προϊόντων από την Amazon μέχρι το 2015. Φυσικά, η δομή τους δεν είναι ακριβώς η ίδια με αυτή των αρχείων καταγραφής κειμένου, αλλά για λόγους απεικόνισης αυτό δεν είναι σημαντικό.

οδηγίες για τη μεταφόρτωση κριτικών Amazon στο ClickHouse

Ας δημιουργήσουμε έναν πίνακα:

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

Στο σύνολο δεδομένων του Amazon υπάρχει μόνο μια ημερομηνία για ανασκόπηση, αλλά δεν υπάρχει ακριβής χρόνος, οπότε ας συμπληρώσουμε αυτά τα δεδομένα με ένα τυχαίο.

Δεν χρειάζεται να κατεβάσετε όλα τα αρχεία tsv και να περιοριστείτε στα πρώτα ~ 10-20 για να αποκτήσετε ένα αρκετά μεγάλο σύνολο δεδομένων που δεν χωράει σε 16 GB μνήμης RAM. Για να ανεβάσω αρχεία TSV χρησιμοποίησα την ακόλουθη εντολή:

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

Σε έναν τυπικό Persistent Disk (που είναι HDD) στο Google Cloud με μέγεθος 1000 GB (πήρα αυτό το μέγεθος κυρίως για να ήταν λίγο υψηλότερη η ταχύτητα, αν και ίσως ένας SSD του απαιτούμενου μεγέθους θα ήταν φθηνότερος) η μεταφόρτωση η ταχύτητα ήταν περίπου ~ 75 MB/sec σε 4 πυρήνες.

  • Πρέπει να κάνω κράτηση ότι εργάζομαι στην Google, αλλά χρησιμοποίησα προσωπικό λογαριασμό και αυτό το άρθρο δεν έχει καμία σχέση με την εργασία μου στην εταιρεία

Θα δημιουργήσω όλες τις απεικονίσεις με αυτό το συγκεκριμένο σύνολο δεδομένων, καθώς αυτό ήταν το μόνο που είχα στη διάθεσή μου.

Εμφάνιση προόδου σάρωσης δεδομένων

Δεδομένου ότι στο ClickHouse θα χρησιμοποιήσουμε μια πλήρη σάρωση σε έναν πίνακα με αρχεία καταγραφής και αυτή η λειτουργία μπορεί να διαρκέσει σημαντικό χρόνο και ενδέχεται να μην παράγει αποτελέσματα για μεγάλο χρονικό διάστημα εάν βρεθούν λίγες αντιστοιχίσεις, συνιστάται να μπορείτε να εμφανίσετε την πρόοδο του ερωτήματος μέχρι να ληφθούν οι πρώτες σειρές με το αποτέλεσμα. Για να γίνει αυτό, υπάρχει μια παράμετρος στη διεπαφή HTTP που σας επιτρέπει να στείλετε την πρόοδο στις κεφαλίδες HTTP: send_progress_in_http_headers=1. Δυστυχώς, η τυπική βιβλιοθήκη Go δεν μπορεί να διαβάσει τις κεφαλίδες καθώς λαμβάνονται, αλλά η διεπαφή HTTP 1.0 (δεν πρέπει να συγχέεται με την 1.1!) υποστηρίζεται από το ClickHouse, ώστε να μπορείτε να ανοίξετε μια ακατέργαστη σύνδεση TCP στο ClickHouse και να τη στείλετε εκεί GET /?query=... HTTP/1.0nn και λάβετε τις κεφαλίδες και το σώμα της απάντησης χωρίς διαφυγή ή κρυπτογράφηση, οπότε σε αυτήν την περίπτωση δεν χρειάζεται καν να χρησιμοποιήσουμε την τυπική βιβλιοθήκη.

Ροή αρχείων καταγραφής από το ClickHouse

Το ClickHouse είχε βελτιστοποίηση για ερωτήματα με ORDER BY για σχετικά μεγάλο χρονικό διάστημα (από το 2019;), οπότε ένα ερώτημα όπως

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

Θα αρχίσει αμέσως να επιστρέφει γραμμές που έχουν την υποσυμβολοσειρά "κάτι" στο μήνυμά τους, χωρίς να περιμένει να τελειώσει η σάρωση.

Επίσης, θα ήταν πολύ βολικό εάν το ίδιο το ClickHouse ακυρώσει το αίτημα όταν έκλεισε η σύνδεση με αυτό, αλλά αυτή δεν είναι η προεπιλεγμένη συμπεριφορά. Η αυτόματη ακύρωση αιτήματος μπορεί να ενεργοποιηθεί χρησιμοποιώντας την επιλογή cancel_http_readonly_queries_on_client_close=1.

Σωστός χειρισμός του SIGPIPE στο Go

Όταν εκτελείτε, ας πούμε, την εντολή some_cmd | head -n 10, ακριβώς πώς η εντολή some_cmd σταματά την εκτέλεση όταν head αφαίρεσε 10 γραμμές; Η απάντηση είναι απλή: πότε head τελειώνει, ο σωλήνας κλείνει και το stdout της εντολής some_cmd αρχίζει να δείχνει, υπό όρους, "στο πουθενά". Οταν some_cmd προσπαθεί να γράψει σε κλειστό σωλήνα, λαμβάνει ένα σήμα SIGPIPE, το οποίο τερματίζει σιωπηλά το πρόγραμμα από προεπιλογή.

Στο Go αυτό συμβαίνει επίσης από προεπιλογή, αλλά ο χειριστής σήματος SIGPIPE εκτυπώνει επίσης "σήμα: SIGPIPE" ή ένα παρόμοιο μήνυμα στο τέλος, και για να διαγράψουμε αυτό το μήνυμα πρέπει απλώς να χειριστούμε το SIGPIPE όπως θέλουμε, δηλαδή απλά αθόρυβα έξοδος:

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

Εμφάνιση πλαισίου μηνύματος

Συχνά θέλετε να δείτε το πλαίσιο στο οποίο παρουσιάστηκε κάποιο σφάλμα (για παράδειγμα, ποιο αίτημα προκάλεσε πανικό ή ποια σχετικά προβλήματα ήταν ορατά πριν από τη συντριβή) και σε grep Αυτό γίνεται χρησιμοποιώντας τις επιλογές -A, -B και -C, οι οποίες εμφανίζουν τον καθορισμένο αριθμό γραμμών μετά, πριν και γύρω από το μήνυμα, αντίστοιχα.

Δυστυχώς, δεν έχω βρει έναν εύκολο τρόπο να κάνω το ίδιο στο ClickHouse, επομένως για να εμφανιστεί το πλαίσιο, ένα πρόσθετο αίτημα όπως αυτό αποστέλλεται σε κάθε γραμμή του αποτελέσματος (οι λεπτομέρειες εξαρτώνται από την ταξινόμηση και από το εάν το πλαίσιο εμφανίζεται πριν ή μετά):

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

Εφόσον το αίτημα αποστέλλεται σχεδόν αμέσως αφού το ClickHouse επιστρέψει την αντίστοιχη γραμμή, καταλήγει στην κρυφή μνήμη και γενικά το αίτημα εκτελείται αρκετά γρήγορα και καταναλώνει λίγη CPU (συνήθως το αίτημα διαρκεί περίπου ~6 ms στην εικονική μου μηχανή).

Εμφάνιση νέων μηνυμάτων σε πραγματικό χρόνο

Για να εμφανίσουμε τα εισερχόμενα μηνύματα σε (σχεδόν) πραγματικό χρόνο, απλώς εκτελούμε το αίτημα μία φορά κάθε λίγα δευτερόλεπτα, θυμόμαστε την τελευταία χρονική σήμανση που συναντήσαμε πριν.

Παραδείγματα εντολών

Πώς μοιάζουν στην πράξη οι τυπικές εντολές logscli;

Εάν κατεβάσατε το σύνολο δεδομένων Amazon που ανέφερα στην αρχή του άρθρου, μπορείτε να εκτελέσετε τις ακόλουθες εντολές:

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

παραπομπές

Ο κωδικός βοηθητικού προγράμματος (χωρίς τεκμηρίωση) είναι διαθέσιμος στο github στη διεύθυνση https://github.com/YuriyNasretdinov/logscli. Θα χαρώ να ακούσω τις σκέψεις σας σχετικά με την ιδέα μου για μια διεπαφή κονσόλας για την προβολή αρχείων καταγραφής με βάση το ClickHouse.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο