Jika Anda pernah menggunakan antarmuka web untuk melihat log, Anda mungkin memperhatikan bagaimana, pada umumnya, antarmuka ini rumit dan (seringkali) tidak terlalu nyaman dan responsif. Beberapa mungkin sudah biasa bagi Anda, beberapa benar-benar buruk, tetapi menurut saya alasan dari semua masalah ini adalah karena kami melakukan pendekatan yang salah terhadap tugas melihat log: kami mencoba membuat antarmuka web di mana CLI (antarmuka baris perintah) bekerja lebih baik. Saya pribadi sangat nyaman bekerja dengan tail, grep, awk dan lain-lain, dan oleh karena itu bagi saya antarmuka yang ideal untuk bekerja dengan log adalah sesuatu yang mirip dengan tail dan grep, tetapi juga dapat digunakan untuk membaca log yang berasal dari banyak server. Tentu saja, membacanya dari ClickHouse!
*menurut pendapat pribadi pengguna habra
Temui logscli
Saya tidak menemukan nama untuk antarmuka saya, dan sejujurnya, ini ada dalam bentuk prototipe, tetapi jika Anda ingin segera melihat kode sumbernya, silakan:
Kemampuan
Я ставил перед собой цель сделать интерфейс, который будет казаться знакомым тем, кто привык к tail/grep, то есть поддерживать следующие вещи:
- Lihat semua log, tanpa memfilter.
- Tinggalkan baris yang berisi substring tetap (flag
-F
уgrep
). - Tinggalkan baris yang cocok dengan ekspresi reguler (flag
-E
уgrep
). - Secara default, tampilan dilakukan dalam urutan kronologis terbalik, karena log terbaru biasanya menjadi perhatian terlebih dahulu.
- Tampilkan konteks di samping setiap baris (opsi
-A
,-B
и-C
уgrep
, masing-masing mencetak N baris sebelum, sesudah, dan di sekitar setiap baris yang cocok). - Lihat log masuk secara real time, dengan atau tanpa pemfilteran (intinya
tail -f | grep
). - Antarmuka harus kompatibel dengan
less
,head
,tail
dan lainnya - secara default, hasil harus dikembalikan tanpa batasan jumlahnya; garis dicetak sebagai aliran selama pengguna tertarik untuk menerimanya; sinyalSIGPIPE
harus secara diam-diam mengganggu streaming log, sama seperti yang mereka lakukantail
,grep
dan utilitas UNIX lainnya.
Implementasi
Saya berasumsi bahwa Anda sudah mengetahui cara mengirimkan log ke ClickHouse. Jika belum, saya sarankan untuk mencobanya
Pertama, Anda perlu memutuskan skema dasar. Karena Anda biasanya ingin menerima log yang diurutkan berdasarkan waktu, tampaknya logis untuk menyimpannya seperti itu. Jika ada banyak kategori log dan semuanya memiliki tipe yang sama, maka Anda dapat menjadikan kategori log sebagai kolom pertama dari kunci utama - ini akan memungkinkan Anda memiliki satu tabel, bukan beberapa, yang akan menjadi nilai tambah yang besar ketika memasukkan ke ClickHouse (pada server dengan hard drive, disarankan untuk memasukkan data tidak lebih dari ~1 kali per detik untuk seluruh server).
Artinya, kita membutuhkan kira-kira skema tabel berikut:
CREATE TABLE logs(
category LowCardinality(String), -- категория логов (опционально)
time DateTime, -- время события
millis UInt16, -- миллисекунды (могут быть и микросекунды, и т.д.): рекомендуется хранить, если событий много, чтобы было легче различать события между собой
..., -- ваши собственные поля, например имя сервера, уровень логирования, и так далее
message String -- текст сообщения
) ENGINE=MergeTree()
ORDER BY (category, time, millis)
Sayangnya, saya tidak dapat segera menemukan sumber terbuka dengan log realistis yang dapat saya ambil dan unduh, jadi saya mengambil ini sebagai contoh.
instruksi untuk mengunggah ulasan Amazon ke ClickHouse
Mari buat tabel:
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
Pada dataset Amazon hanya ada tanggal peninjauannya, namun belum ada waktu pastinya, jadi mari kita isi data ini dengan randon.
Anda tidak perlu mengunduh semua file tsv dan membatasi diri Anda pada ~10-20 file pertama untuk mendapatkan kumpulan data yang cukup besar yang tidak dapat dimasukkan ke dalam RAM 16 GB. Untuk mengunggah file TSV saya menggunakan perintah berikut:
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
Pada Persistent Disk standar (yang merupakan HDD) di Google Cloud dengan ukuran 1000 GB (saya mengambil ukuran ini terutama agar kecepatannya sedikit lebih tinggi, meskipun mungkin SSD dengan ukuran yang diperlukan akan lebih murah) unggahan kecepatannya sekitar ~ 75 MB/detik pada 4 core.
- Saya harus membuat reservasi bahwa saya bekerja di Google, tetapi saya menggunakan akun pribadi dan artikel ini tidak ada hubungannya dengan pekerjaan saya di perusahaan
Saya akan membuat semua ilustrasi dengan kumpulan data khusus ini, karena hanya ini yang saya miliki.
Tampilkan kemajuan pemindaian data
Karena di ClickHouse kita akan menggunakan pemindaian penuh pada tabel dengan log, dan operasi ini dapat memakan banyak waktu dan mungkin tidak memberikan hasil apa pun untuk waktu yang lama jika ditemukan sedikit kecocokan, disarankan untuk dapat menampilkan kemajuan kueri hingga baris pertama dengan hasilnya diterima. Untuk melakukan ini, ada parameter di antarmuka HTTP yang memungkinkan Anda mengirim kemajuan dalam header HTTP: send_progress_in_http_headers=1
. Sayangnya, pustaka Go standar tidak dapat membaca header saat diterima, namun antarmuka HTTP 1.0 (jangan bingung dengan 1.1!) didukung oleh ClickHouse, sehingga Anda dapat membuka koneksi TCP mentah ke ClickHouse dan mengirimkannya ke sana GET /?query=... HTTP/1.0nn
dan menerima header dan isi respons tanpa pelolosan atau enkripsi apa pun, jadi dalam hal ini kita bahkan tidak perlu menggunakan perpustakaan standar.
Streaming log dari ClickHouse
ClickHouse telah melakukan optimasi untuk query dengan ORDER BY dalam waktu yang relatif lama (sejak 2019?), jadi query seperti
SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESC
Ini akan segera mulai mengembalikan baris yang memiliki substring "sesuatu" dalam pesannya, tanpa menunggu pemindaian selesai.
Selain itu, akan sangat mudah jika ClickHouse sendiri membatalkan permintaan saat koneksi ditutup, tetapi ini bukan perilaku default. Pembatalan permintaan otomatis dapat diaktifkan menggunakan opsi ini cancel_http_readonly_queries_on_client_close=1
.
Penanganan SIGPIPE yang benar di Go
Saat Anda menjalankan, katakanlah, perintah some_cmd | head -n 10
, persis bagaimana perintahnya some_cmd
menghentikan eksekusi kapan head
dikurangi 10 baris? Jawabannya sederhana: kapan head
berakhir, pipa menutup, dan stdout dari perintah some_cmd mulai menunjuk, dengan syarat, "ke mana-mana". Kapan some_cmd
mencoba menulis ke pipa tertutup,
Di Go, hal ini juga terjadi secara default, tetapi pengendali sinyal SIGPIPE juga mencetak "sinyal: SIGPIPE" atau pesan serupa di akhir, dan untuk menghapus pesan ini kita hanya perlu menangani SIGPIPE sendiri sesuai keinginan kita, yaitu diam-diam KELUAR:
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGPIPE)
go func() {
<-ch
os.Exit(0)
}()
Tampilkan konteks pesan
Seringkali Anda ingin melihat konteks di mana beberapa kesalahan terjadi (misalnya, permintaan mana yang menyebabkan kepanikan, atau masalah terkait apa yang terlihat sebelum kerusakan), dan di grep
Hal ini dilakukan dengan menggunakan opsi -A, -B, dan -C, yang masing-masing menampilkan jumlah baris tertentu setelah, sebelum, dan di sekitar pesan.
Sayangnya, saya belum menemukan cara mudah untuk melakukan hal yang sama di ClickHouse, jadi untuk menampilkan konteksnya, permintaan tambahan seperti ini dikirim ke setiap baris hasil (detailnya bergantung pada penyortiran dan apakah konteksnya ditampilkan sebelumnya atau setelahnya):
SELECT time,millis,review_body FROM amazon
WHERE (time = 'ВРЕМЯ_СОБЫТИЯ' AND millis < МИЛЛИСЕКУНДЫ_СОБЫТИЯ) OR (time < 'ВРЕМЯ_СОБЫТИЯ')
ORDER BY time DESC, millis DESC
LIMIT КОЛИЧЕСТВО_СТРОК_КОНТЕКСТА
SETTINGS max_threads=1
Karena permintaan dikirim segera setelah ClickHouse mengembalikan baris yang sesuai, permintaan tersebut berakhir di cache dan secara umum permintaan dijalankan cukup cepat dan menghabiskan sedikit CPU (biasanya permintaan memerlukan waktu sekitar ~6 ms di mesin virtual saya).
Tampilkan pesan baru secara real time
Untuk menampilkan pesan masuk (hampir) real-time, kita cukup menjalankan permintaan setiap beberapa detik sekali, mengingat stempel waktu terakhir yang kita temui sebelumnya.
Contoh perintah
Seperti apa perintah logscli pada umumnya dalam praktiknya?
Jika Anda mengunduh kumpulan data Amazon yang saya sebutkan di awal artikel, Anda dapat menjalankan perintah berikut:
# Показать строки, где встречается слово 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
referensi
Kode utilitas (tanpa dokumentasi) tersedia di github di
Sumber: www.habr.com