【夜鶯監(jiān)控】從日志中提取指標(biāo)的瑞士軍刀

【夜鶯監(jiān)控】從日志中提取指標(biāo)的瑞士軍刀

mtail是谷歌開(kāi)源的一款從應(yīng)用日志提取 metrics 的工具,它會(huì)實(shí)時(shí)讀取應(yīng)用程序的日志,然后通過(guò)自己編寫(xiě)的腳本分析日志,最終生成時(shí)間序列的指標(biāo),項(xiàng)目地址是:https://github.com/google/mtail。

夜鶯的Categraf對(duì)日志指標(biāo)的收集也是采用的 mtail,不過(guò)做了一些優(yōu)化,具體優(yōu)化了什么我們慢慢道來(lái)。

現(xiàn)在,我們先從谷歌的mtail開(kāi)始聊起,再慢慢聊到夜鶯的 mtail 插件。

mtail 的安裝

前面已經(jīng)對(duì)mtail做了簡(jiǎn)短的介紹,其實(shí)那就是全部。

所以,我們直接從安裝開(kāi)始。

從https://github.com/google/mtail/releases下載需要的版本,操作如下:

# 下載$ wget https://github.com/google/mtail/releases/download/v3.0.0-rc51/mtail_3.0.0-rc51_Linux_x86_64.tar.gz$ tar xf mtail_3.0.0-rc51_Linux_x86_64.tar.gz$ cp mtail /usr/local/bin# 查看mtail版本$ mtail --versionmtail version 3.0.0-rc51 git revision 6fdbf8ec96a63c674c53148eeb9ec96043a2ec9c go version go1.19.4 go arch amd64 go os linux# mtail后臺(tái)啟動(dòng)$ nohup mtail -port 3903 -logtostderr -progs test.mtail -logs test.log  默認(rèn)端口是3903$ nohup ./mtail -progs test.mtail -logs test.log  查看是否啟動(dòng)成功$ ps -ef | grep mtail# 查看mtail的幫助文檔$ mtail -h

mtail 參數(shù)詳解

安裝完mtail之后,如果對(duì)mtail的參數(shù)一無(wú)所知的話,也就不知道如何下手了,本小節(jié)就帶大家來(lái)了解一下 mtail 有哪些參數(shù)。

我們可以通過(guò)mtail -h來(lái)查看mtail支持的參數(shù)列表,下面我對(duì)這些參數(shù)加一些中文注釋?zhuān)瑧?yīng)該能夠幫助你了解它們的意思了。

$ mtail -hmtail version 3.0.0-rc51 git revision 6fdbf8ec96a63c674c53148eeb9ec96043a2ec9c go version go1.19.4 go arch amd64 go os linuxUsage:-address string # 綁定HTTP監(jiān)聽(tīng)器的主機(jī)或者IP地址-alsologtostderr # 記錄標(biāo)準(zhǔn)錯(cuò)誤和文件-block_profile_rate int # 報(bào)告goroutine阻塞事件之前的阻塞時(shí)間的納秒數(shù)。0表示關(guān)閉。-collectd_prefix string # 發(fā)送給collectd的指標(biāo)前綴-collectd_socketpath string # collectd socket路徑,用于向其寫(xiě)入metrics-compile_only # 僅禪師編譯mtail腳本程序,不執(zhí)行-disable_fsnotify # 是否禁用文件動(dòng)態(tài)發(fā)現(xiàn)機(jī)制。為true時(shí),不會(huì)監(jiān)聽(tīng)動(dòng)態(tài)加載發(fā)現(xiàn)的新文件,只會(huì)監(jiān)聽(tīng)程序啟動(dòng)時(shí)的文件。-dump_ast # 解析后dump程序的AST(默認(rèn)到/tmp/mtail.INFO)-dump_ast_types # 在類(lèi)型檢查之后dump帶有類(lèi)型注釋的程序的AST(默認(rèn)到/tmp/mtail.INFO)-dump_bytecode # dump程序字節(jié)碼-emit_metric_timestamp # 發(fā)出metric的記錄時(shí)間戳。如果禁用(默認(rèn)設(shè)置),則不會(huì)向收集器發(fā)送顯式時(shí)間戳。-emit_prog_label # 在導(dǎo)出的變量里面展示prog對(duì)應(yīng)的標(biāo)簽。默認(rèn)為true-expired_metrics_gc_interval duration # metric的垃圾收集器運(yùn)行間隔(默認(rèn)為1h0m0s)-graphite_host_port string # graphite carbon服務(wù)器地址,格式Host:port。用于向graphite carbon服務(wù)器寫(xiě)入metrics-graphite_prefix string # 發(fā)送給graphite指標(biāo)的metrics前綴-http_debugging_endpoint # 是否開(kāi)啟調(diào)式接口(/debug/*),默認(rèn)開(kāi)啟-http_info_endpoint # 是否開(kāi)始info接口(/progz,/varz),默認(rèn)開(kāi)啟-ignore_filename_regex_pattern string # 需要忽略的日志文件名字,支持正則表達(dá)式。使用場(chǎng)景:當(dāng)-logs參數(shù)指定的為一個(gè)目錄時(shí),可以使用ignore_filename_regex_pattern 參數(shù)來(lái)忽略一部分文件-jaeger_endpoint string # 如果設(shè)為true,可以將跟蹤導(dǎo)出到Jaeger跟蹤收集器。使用–jaeger_endpoint標(biāo)志指定Jaeger端點(diǎn)URL-log_backtrace_at value # 當(dāng)日志記錄命中設(shè)置的行N時(shí),發(fā)出堆棧跟蹤-log_dir string # mtail程序的日志文件的目錄,與logtostderr作用類(lèi)似,如果同時(shí)配置了logtostderr參數(shù),則log_dir參數(shù)無(wú)效-logs value # 監(jiān)控的日志文件列表,可以使用,分隔多個(gè)文件,也可以多次使用-logs參數(shù),也可以指定一個(gè)文件目錄,支持通配符*,指定文件目錄時(shí)需要對(duì)目錄使用單引號(hào)。-logtostderr # 直接輸出標(biāo)準(zhǔn)錯(cuò)誤信息,編譯問(wèn)題也直接輸出-max_recursion_depth int # 以解析的標(biāo)記來(lái)衡量mtail語(yǔ)句的最大長(zhǎng)度。過(guò)長(zhǎng)的mtail表達(dá)式可能會(huì)導(dǎo)致編譯和運(yùn)行時(shí)的性能問(wèn)題。(默認(rèn)為100)-max_regexp_length int # 一個(gè)mtail regexp表達(dá)式的最大長(zhǎng)度。過(guò)長(zhǎng)的模式可能會(huì)導(dǎo)致編譯和運(yùn)行時(shí)的性能問(wèn)題。(默認(rèn)為1024)-metric_push_interval duration # metric推送時(shí)間間隔,單位:秒,默認(rèn)60秒-metric_push_interval_seconds int # 棄用,用--metric_push_interval代替-metric_push_write_deadline duration # 在出現(xiàn)錯(cuò)誤退出之前等待推送成功的時(shí)間。(默認(rèn)10s)-mtailDebug int # 設(shè)置解析器debug級(jí)別-mutex_profile_fraction int # 報(bào)告的互斥爭(zhēng)奪事件的比例。 0將關(guān)閉-one_shot # 此參數(shù)將編譯并運(yùn)行mtail程序,然后從指定的文件開(kāi)頭開(kāi)始讀取日志(從頭開(kāi)始讀取日志,不是實(shí)時(shí)tail),然后將收集的所有metrics打印到日志中。此參數(shù)用于驗(yàn)證mtail程序是否有預(yù)期輸出,不用于生產(chǎn)環(huán)境。-one_shot_format string # 與-one_shot一起使用的格式。這只是一個(gè)調(diào)試標(biāo)志,不適合生產(chǎn)使用。支持的格式: json, prometheus. (默認(rèn)為 "json")-override_timezone string # 設(shè)置時(shí)區(qū),如果使用此參數(shù),將在時(shí)間戳轉(zhuǎn)換中使用指定的時(shí)區(qū)來(lái)替代UTC-poll_interval duration # 設(shè)置輪詢(xún)所有日志文件以獲取數(shù)據(jù)的間隔;必須為正,如果為零將禁用輪詢(xún)。使用輪詢(xún)模式,將僅輪詢(xún)?cè)趍tail啟動(dòng)時(shí)找到的文件-poll_log_interval duration # 設(shè)置找到所有匹配的日志文件進(jìn)行輪詢(xún)的時(shí)間間隔;必須是正數(shù),或者是0來(lái)禁用輪詢(xún)。 在輪詢(xún)模式下,只有在mtail啟動(dòng)時(shí)發(fā)現(xiàn)的文件會(huì)被輪詢(xún)。(默認(rèn)250ms)-port string # 監(jiān)聽(tīng)的http端口,默認(rèn)3903-progs string # mtail腳本程序所在路徑-stale_log_gc_interval duration # stale的垃圾收集器運(yùn)行間隔(默認(rèn)為1h0m0s)-statsd_hostport string # statsd地址,格式Host:port。用于向statsd寫(xiě)入metrics-statsd_prefix string # 發(fā)送給statsd指標(biāo)的metrics前綴-stderrthreshold value # 嚴(yán)重性級(jí)別達(dá)到閾值以上的日志信息除了寫(xiě)入日志文件以外,還要輸出到stderr。各嚴(yán)重性級(jí)別對(duì)應(yīng)的數(shù)值:INFO—0,WARNING—1,ERROR—2,F(xiàn)ATAL—3,默認(rèn)值為2.-syslog_use_current_year # 如果時(shí)間戳沒(méi)有年份,則用當(dāng)前年替代。(默認(rèn)為true)-trace_sample_period int # 用于設(shè)置跟蹤的采樣頻率和發(fā)送到收集器的頻率。將其設(shè)置為100,則100條收集一條追蹤。-unix_socket string # socket監(jiān)控地址-v value # v日志的日志級(jí)別,該設(shè)置可能被 vmodule標(biāo)志給覆蓋.默認(rèn)為0.-version # 打印mtail版本-vm_logs_runtime_errors # 啟用運(yùn)行時(shí)錯(cuò)誤的記錄到標(biāo)準(zhǔn)日志。 如果設(shè)置為false,則只將錯(cuò)誤打印到HTTP控制臺(tái)。(默認(rèn)為true)-vmodule value # 按文件或模塊來(lái)設(shè)置日志級(jí)別,如:-vmodule=mapreduce=2,file=1,gfs*=3

配置參數(shù)非常多,一般情況下我們使用的也就那幾個(gè),如下:

nohup ./mtail -progs test.mtail -logs test.log &

指定 mtail 腳本以及日志目錄即可。

mtail 腳本語(yǔ)法

在https://github.com/google/mtail/blob/main/docs/Programming-Guide.md處對(duì)腳本語(yǔ)法有相應(yīng)的介紹,這里做一個(gè)簡(jiǎn)單的介紹。

腳本標(biāo)準(zhǔn)的格式如下:

COND {ACTION}

其中COND是一個(gè)條件表達(dá)式,可以是正則表達(dá)式,也可以是 Boolean 類(lèi)型的條件語(yǔ)句,如下:

/foo/ {ACTION1}variable > 0{ACTION2}/foo/ && variable > 0{ACTION3}

COND表達(dá)式可用的運(yùn)算符如下:

  1. 關(guān)系運(yùn)算符: , >= , == , != , =~ , !~ , || , && , !
  2. 算術(shù)運(yùn)算符:| , & , ^ , + , – , * , /, > , **

另外,ACTION是具體的操作,如下表示從日志中匹配到 foo 字段,就給相應(yīng)的指標(biāo) foo_total 的值就加 1:

counter foo_total/foo/ { foo_total++}

對(duì)于指標(biāo),可以用= , += , ++ , –等運(yùn)算符進(jìn)行操作。

mtail的目的是從日志中提取信息并將其傳遞到監(jiān)控系統(tǒng)。因此,必須導(dǎo)出指標(biāo)變量并命名,命名可以使用counter、histogram、gauge指標(biāo)類(lèi)型,并且命名的變量必須在COND腳本之前。

  1. Counter(計(jì)數(shù)器):用于記錄單調(diào)遞增的值,例如請(qǐng)求數(shù)、錯(cuò)誤數(shù)等。
  2. Gauge(儀表):用于記錄可增可減的值,例如 CPU 使用率、內(nèi)存使用量等。
  3. Histogram(直方圖):用于記錄數(shù)據(jù)的分布情況,例如請(qǐng)求延遲、響應(yīng)大小等。

我們知道,拿 Prometheus 來(lái)說(shuō),除了上面的三種指標(biāo)類(lèi)型之外還有一個(gè)Summary的指標(biāo)類(lèi)型,為什么 mtail 沒(méi)有呢?

因?yàn)樵?Prometheus 中,summary 指標(biāo)類(lèi)型用于記錄數(shù)據(jù)的分布情況,并計(jì)算出更多的統(tǒng)計(jì)信息,例如平均值、中位數(shù)、標(biāo)準(zhǔn)差等。但是,由于 mtail 是從日志文件中提取指標(biāo),而不是直接從應(yīng)用程序中提取指標(biāo),因此沒(méi)有必要使用 summary 指標(biāo)類(lèi)型。

高階用法

變量定義

對(duì)于在一個(gè)腳本中需要重復(fù)使用的表達(dá)式,可以將其定義為一個(gè)變量,后續(xù)可以直接使用變量。

counter duplicate_leaseconst IP /d+(.d+){3}/const MATCH_IP /(?P<ip>/ + IP + /)//uid lease / + MATCH_IP + / for client .* is duplicate on / { duplicate_lease++}</ip>

這是開(kāi)發(fā)中常用的手段。

解析時(shí)間戳

mtail 會(huì)為每一個(gè)日志事件都賦予一個(gè)時(shí)間戳,如果日志里沒(méi)有時(shí)間戳,mtail 會(huì)為本次日志事件賦予一個(gè)當(dāng)前的日志時(shí)間。

除此之外,如果日志里的時(shí)間戳不是標(biāo)準(zhǔn)時(shí)間或者其他情況,可以使用 strptime 對(duì)其進(jìn)行解析,如下:

/^/ +/(?P<date>d{4}/d{2}/d{2} d{2}:d{2}:d{2}) / +/.*/ +/$/ {strptime($date, "2006/01/02 15:04:05")}</date>

條件判斷

/pattern/ { action }是 mtail 程序中正常的條件控制流結(jié)構(gòu)。

如果模式匹配,那么該塊中的動(dòng)作就會(huì)被執(zhí)行。如果模式不匹配,則跳過(guò)該塊。

else關(guān)鍵字允許程序在模式不匹配的情況下執(zhí)行動(dòng)作。

/pattern/ {action} else {alternative}

除此之外,還可以使用 otherwise 來(lái)處理沒(méi)有匹配到的規(guī)則,如下:

{/pattern1/ { _action1_ }/pattern2/ { _action2_ }otherwise { _action3_ }}

這種語(yǔ)法類(lèi)似于switch case default語(yǔ)法。

精準(zhǔn)匹配

上面的/pattern/ { _action_ }形式隱含地匹配了當(dāng)前的輸入日志行。

如果想與另一個(gè)字符串變量匹配,可以使用=~操作符,或者用!~來(lái)否定匹配,像這樣:

$1 =~ /GET/ {...}

解析非數(shù)字類(lèi)型的數(shù)字字段

有時(shí)候遇到的日志里輸出的數(shù)字是字符串,而非數(shù)字,mtail 可以對(duì)其進(jìn)行解析,如下:

counter total/^[a-z]+ ((?P<response_size>d+)|-)$/ {$1 != "-" {total = $response_size}}</response_size>

解析帶有額外字符的數(shù)字

一些日志包含除了包含數(shù)字,還包含分隔符,我們可以用 subst 函數(shù)刪除它們:

/sent (?P<sent>[d,]+) bytesreceived (?P<received>[d,]+) bytes/ {# Sum total bytes across all sessions for this processbytes_total["sent"] += int(subst(",", "", $sent))bytes_total["received"] += int(subst(",", "", $received))}</received></sent>

過(guò)濾操作

如果你想過(guò)濾一些不必要的日志被mtail采集,你可以使用stop,如下:

getfilename() !~ /apache.access.?log/ {stop}

重寫(xiě)操作

一些日志,如網(wǎng)絡(luò)服務(wù)器日志,描述了一些常見(jiàn)的元素,其中有獨(dú)特的標(biāo)識(shí)符,如果不加處理,會(huì)導(dǎo)致大量的度量衡鍵,而沒(méi)有有用的計(jì)數(shù)。可以使用subst()將模式作為第一個(gè)參數(shù)來(lái)重新編寫(xiě)這些捕獲組

hidden text routecounter http_requests_total by method, route/(?P<method>S+)/ {route = subst(//d+/, "/:num", $url)http_requests_total[method][route]++}</method>

這里我們把$url中/后面的任何數(shù)字部分替換為字面字符串/:num,所以我們最終只計(jì)算 URL 路由的靜態(tài)部分。

mtail 實(shí)操

說(shuō)一千,道一萬(wàn),不如真正來(lái)一遍。

當(dāng)然,我這里也不會(huì)把上面說(shuō)的都來(lái)一次。

為了方便闡述,我把本次操作的腳本都放到~/Desktop/mtail目錄中。

單日志采集

# 創(chuàng)建prog1,里面用于保存日志處理的規(guī)則腳本$ mkdir prog1# 在prog1里創(chuàng)建prog1.mtail文件并寫(xiě)入以下內(nèi)容$ cat prog1.mtailcounter foo_count/foo/{foo_count++}# 創(chuàng)建log1目錄$ mkdir log1# 在log1中創(chuàng)建a.log文件¥ touch a.log# 啟動(dòng)mtail$ mtail -progs ~/Desktop/mtail/prog1 -logs ~/Desktop/mtail/log1/a.log# 向a.log中寫(xiě)入foo$ echo "foo" &gt; ~/Desktop/mtail/log1/a.log# 查看指標(biāo)明細(xì)$ curl 127.0.0.1:3903/metrics# HELP foo_count defined at prog1.mtail:1:9-17# TYPE foo_count counterfoo_count{prog="prog1.mtail"} 1 # 可以看到foo_count指標(biāo)數(shù)為1了

多日志采集

如果多日志在同一個(gè)文件夾里,這時(shí)候采集的指標(biāo)就可能混淆。

# 在log1目錄中創(chuàng)建b.log$ touch b.log# 然后為b.log重新創(chuàng)建一個(gè)指標(biāo)腳本$ cat prog1/prog2.mtailcounter bar_count/bar/{bar_count++}# 啟動(dòng)mtail$ mtail -progs ~/Desktop/mtail/prog1 -logs ~/Desktop/mtail/log1/a.log -logs ~/Desktop/mtail/log1/b.log# 向b.log寫(xiě)入日志$ echo "bar" &gt;&gt;~/Desktop/mtail/log1/b.log# 查看指標(biāo)$ curl 127.0.0.1:3903/metrics# HELP bar_count defined at prog2.mtail:1:9-17# TYPE bar_count counterbar_count{prog="prog2.mtail"} 2# HELP foo_count defined at prog1.mtail:1:9-17# TYPE foo_count counterfoo_count{prog="prog1.mtail"} 0

可以看到能正常收集指標(biāo),但是如果我們向 a.log 也寫(xiě)入 bar 日志,指標(biāo)會(huì)增加嗎?

# 向a.log寫(xiě)入日志$ echo "bar" &gt;&gt;~/Desktop/mtail/log1/a.log# 查看指標(biāo)$ curl 127.0.0.1:3903/metrics# HELP bar_count defined at prog2.mtail:1:9-17# TYPE bar_count counterbar_count{prog="prog2.mtail"} 3# HELP foo_count defined at prog1.mtail:1:9-17# TYPE foo_count counterfoo_count{prog="prog1.mtail"} 0

可以看到指標(biāo)依然會(huì)增加。其實(shí)我們的期望是prog1.mtail只收集a.log的日志指標(biāo),prog2.mtail只收集b.log的指標(biāo),不要相互影響。

如果要解決這個(gè)問(wèn)題,就需要啟動(dòng)不同的mtail才行。換句話說(shuō)有多少日志文件,如果想分開(kāi)收集,則要啟動(dòng)多少個(gè)mtail,可以想想這是一個(gè)非常恐怖的事情。

鑒于此,Categraf 對(duì) mtail 插件做了一些優(yōu)化,優(yōu)化后的 mtail 插件可以做到一個(gè) Categraf 進(jìn)程同時(shí)解析多個(gè)服務(wù)的日志,改造后的示例圖如下:

【夜鶯監(jiān)控】從日志中提取指標(biāo)的瑞士軍刀

Categraf 操作

在前面的夜鶯監(jiān)控系列中,對(duì) Categraf 基本都有一個(gè)印象。在默認(rèn)情況下,它的設(shè)置位于 conf 目錄中,插件則被放置在名稱(chēng)以 input 開(kāi)頭的文件夾中。

我們進(jìn)入input.mtail文件夾,編輯mtail.toml并增加如下配置:

[[instances]]progs = "/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/prog1"logs = ["/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/log1/a.log"]# override_timezone = "Asia/Shanghai"# emit_metric_timestamp = "true" #string type[[instances]]progs = "/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/prog2"logs = ["/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/log1/b.log"]# override_timezone = "Asia/Shanghai"# emit_metric_timestamp = "true" # string type

然后添加需要的目錄以及腳本:

# 創(chuàng)建文件夾$ mkdir {prog1,prog2,log1}# 增加規(guī)則文件$ cat prog1/a.mtailcounter foo_count/foo/ {foo_count++}$ cat prog2/b.mtailcounter bar_count/bar/ {bar_count++}# 增加日志文件$ touch {log1/a.log,log1/b.log}

啟動(dòng) categraf:

# 使用測(cè)試模式啟動(dòng)$ ./categraf -test -inputs mtail

然后往a.log寫(xiě)入foo日志。

echo "foo" &gt;&gt; log1/a.log

然后看到指標(biāo)增加了:

【夜鶯監(jiān)控】從日志中提取指標(biāo)的瑞士軍刀

再往b.log寫(xiě)入bar日志。

echo "bar" &gt;&gt; log1/b.log

bar_count的指標(biāo)也相應(yīng)增加了。

【夜鶯監(jiān)控】從日志中提取指標(biāo)的瑞士軍刀

那如果我們向a.log增加bar的日志,bar_count會(huì)增加么?我們來(lái)測(cè)試一下:

echo "bar" &gt;&gt; log1/a.log

通過(guò)觀察bar_count指標(biāo)不會(huì)增加。

【夜鶯監(jiān)控】從日志中提取指標(biāo)的瑞士軍刀

Categraf 就完美解決了不同日志指標(biāo)錯(cuò)亂的問(wèn)題。

除了正常的處理指標(biāo),如果想給不同的instance指定label,也是可以的,如下:

[[instances]]progs = "/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/prog1"logs = ["/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/log1/a.log"]labels = {"app"= "foo"}# override_timezone = "Asia/Shanghai"# emit_metric_timestamp = "true" #string type[[instances]]progs = "/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/prog2"logs = ["/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/log1/b.log"]labels = {"app"= "bar"}# override_timezone = "Asia/Shanghai"# emit_metric_timestamp = "true" # string type

重啟 Categraf 就可以看到指標(biāo)多了一個(gè) label。

【夜鶯監(jiān)控】從日志中提取指標(biāo)的瑞士軍刀

其他的腳本語(yǔ)法和原生的 mtail 一致,這里不再追溯了。

總結(jié)

Categraf has made some optimizations to mtail compared to Google’s mtail, which can better handle multi-log problems.。而且 categraf 本身集成了很多插件,都可以統(tǒng)一使用它實(shí)現(xiàn)。

另外,還是相同的問(wèn)題,假設(shè)插件開(kāi)啟比較多,categraf 的具體性能如何以及會(huì)不會(huì)影響主機(jī)的整體性能,這還有待研究。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊10 分享