如果您曾經使用 Web 介面查看日誌,那麼您可能已經注意到,這些介面通常很麻煩,而且(通常)不太方便且響應速度不快。 有些你可以習慣,有些絕對糟糕,但在我看來,所有問題的原因是我們錯誤地處理了查看日誌的任務:我們嘗試創建一個 Web 介面,其中 CLI(命令列介面)效果更好。 我個人非常喜歡使用 tail、grep、awk 等,因此對我來說,處理日誌的理想介面是類似於 tail 和 grep 的東西,但它也可以用於讀取來自許多伺服器的日誌。 那當然是從 ClickHouse 讀它們!
*根據habra用戶的個人意見
認識 Logscli
我沒有為我的介面起一個名字,而且,說實話,它是以原型的形式存在的,但如果你想立即看到原始程式碼,那麼歡迎你:
功能
我的目標是製作一個對於習慣 tail/grep 的人來說似乎很熟悉的介面,即支援以下內容:
- 查看所有日誌,不進行過濾。
- 留下包含固定子字串的行(標誌
-F
уgrep
). - 留下與正規表示式相符的行(標誌
-E
уgrep
). - 預設情況下,檢視是按時間倒序排列的,因為最近的日誌通常是首先感興趣的。
- 顯示每行旁邊的上下文(選項
-A
,-B
и-C
уgrep
,分別在每個匹配行之前、之後和周圍列印 N 行)。 - 即時查看傳入日誌,帶或不帶過濾(本質上是
tail -f | grep
). - 此介面必須相容
less
,head
,tail
和其他 - 預設情況下,應傳回結果,且不限制其數量; 只要用戶有興趣接收它們,行就會作為流打印; 訊號SIGPIPE
應該像他們一樣默默地中斷日誌流tail
,grep
和其他 UNIX 實用程式。
履行
我假設您已經以某種方式知道如何將日誌傳遞到 ClickHouse。 如果沒有的話我建議嘗試一下
首先,您需要決定基本方案。 由於您通常希望接收按時間排序的日誌,因此以這種方式儲存它們似乎是合乎邏輯的。 如果有很多日誌類別並且它們都屬於同一類型,那麼您可以將日誌類別作為主鍵的第一列 - 這將允許您擁有一個表而不是多個表,這將是一個很大的優點插入ClickHouse(在有硬碟的伺服器上,建議每秒插入資料不超過~1次 對於整個伺服器).
也就是說,我們大約需要下表方案:
CREATE TABLE logs(
category LowCardinality(String), -- категория логов (опционально)
time DateTime, -- время события
millis UInt16, -- миллисекунды (могут быть и микросекунды, и т.д.): рекомендуется хранить, если событий много, чтобы было легче различать события между собой
..., -- ваши собственные поля, например имя сервера, уровень логирования, и так далее
message String -- текст сообщения
) ENGINE=MergeTree()
ORDER BY (category, time, millis)
不幸的是,我無法立即找到任何具有可以抓取和下載的真實日誌的開源程式碼,所以我以此作為範例
將亞馬遜評論上傳到 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
在亞馬遜資料集中,只有評論日期,但沒有確切時間,所以讓我們用隨機數字填充此資料。
您不必下載所有 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
在 Google Cloud 中大小為 1000 GB 的標準持久性磁碟(HDD)上(我選擇這個大小主要是為了速度更高一點,儘管所需大小的 SSD 可能會更便宜)上傳75 核心上的速度約為4 MB /秒。
- 我必須預約我在Google工作,但我使用的是個人帳戶,本文與我在公司的工作無關
我將用這個特定的數據集製作所有插圖,因為這是我手頭上的全部。
顯示資料掃描進度
由於在 ClickHouse 中,我們將對帶有日誌的表使用完整掃描,並且此操作可能會花費大量時間,並且如果找到很少的匹配項,則可能很長時間不會產生任何結果,因此建議能夠顯示查詢的進度,直到收到結果的第一行。 為此,HTTP 介面中有一個參數可讓您在 HTTP 標頭中傳送進度: send_progress_in_http_headers=1
。 不幸的是,標準 Go 庫無法讀取收到的標頭,但 ClickHouse 支援 HTTP 1.0 介面(不要與 1.1 混淆!),因此您可以打開到 ClickHouse 的原始 TCP 連接並將其發送到那裡 GET /?query=... HTTP/1.0nn
並在沒有任何轉義或加密的情況下接收回應標頭和正文,因此在這種情況下我們甚至不需要使用標準函式庫。
從 ClickHouse 串流日誌
ClickHouse 在相對較長的時間內(從 2019 年開始?)對 ORDER BY 查詢進行了優化,因此像這樣的查詢
SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESC
它將立即開始傳回訊息中包含子字串「something」的行,而無需等待掃描完成。
另外,如果 ClickHouse 本身在連線關閉時取消請求,那會非常方便,但這不是預設行為。 可以使用以下選項啟用自動請求取消 cancel_http_readonly_queries_on_client_close=1
.
Go 中 SIGPIPE 的正確處理
當你執行命令時 some_cmd | head -n 10
,具體如何命令 some_cmd
停止執行時 head
減去10行? 答案很簡單:當 head
結束,管道關閉,some_cmd 命令的標準輸出開始有條件地指向「無處可去」。 什麼時候 some_cmd
試著寫入封閉的管道,
在Go 中,預設也會發生這種情況,但是SIGPIPE 訊號處理程序也會在末尾列印「signal: 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 毫秒)。
即時顯示新訊息
為了(幾乎)即時顯示傳入訊息,我們只需每隔幾秒鐘執行一次請求,記住我們之前遇到的最後一個時間戳。
命令示例
典型的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 上找到:
來源: www.habr.com