最近在整理對于各個(gè)模塊的監(jiān)控,需要有一定的實(shí)時(shí)性。比如,需要獲取最近幾分鐘內(nèi)的日志,然后看某些請求的數(shù)量以及響應(yīng)時(shí)間是否符合要求。但是,線上服務(wù)的日志,通常都是按照小時(shí)粒度進(jìn)行切分的,你不可能對一個(gè)文件進(jìn)行直接的過濾操作。在此之前需要解決一個(gè)問題:在一份文件中,獲取最近一段時(shí)間的日志。
當(dāng)然,還有一個(gè)最最基礎(chǔ)的問題:你的日志內(nèi)容里面是表示時(shí)間的字段的。(不打時(shí)間和請求ID的日志簡直就是耍流氓!)
我一開始的想法是:估算平均請求壓力下,每5分鐘的日志會有多少條,然后直接將cat替換為tail -n XXX就可以了。雖然修改起來很方便,但是是有明顯缺陷的:
- 隨著流量變化,
tail出來的日志的時(shí)間粒度是不一樣的。如果用來監(jiān)控實(shí)時(shí)請求響應(yīng)時(shí)間還算能接受,用來監(jiān)控請求量就不行了; - 如果以后模塊升級,增加或者減少了請求日志,
tail出來的數(shù)字需要不斷調(diào)整。
看來還是要精確的獲取某個(gè)時(shí)間段的日志才行。其實(shí)思路還是比較清晰的:
- 計(jì)算出
開始時(shí)間和結(jié)束時(shí)間兩個(gè)字段 - 提取日志行中的
日志時(shí)間 - 比較三個(gè)值,如果日志行的時(shí)間符合要求,則將其打印,作為過濾程序的輸入
- 執(zhí)行數(shù)日志數(shù)量或者統(tǒng)計(jì)請求響應(yīng)時(shí)間的命令
對于步驟1,使用date命令就可以獲取,這個(gè)簡單。
對于步驟2,一般日志中的時(shí)間都會比較在日志前面幾個(gè)字段,比較好提取,也不難。
步驟4嘛,就看需求是什么了,如果是獲取請求數(shù)目,直接用grep和wc -l就OK了。如果涉及到提取日志字段,簡單的也可以用cut搞定,復(fù)雜就得用grep或者awk了。
最關(guān)鍵是步驟3如何實(shí)現(xiàn),我想到的是在awk中進(jìn)行邏輯判斷,獲取日志中的時(shí)間字段不難,但是如果時(shí)間字段是通過多個(gè)字段拼接而來的,比如2014-05-02和17:25:00,怎么把他們放到一個(gè)變量里面呢?要是有像sprintf這樣的函數(shù)就好了,沒想到,還真有!類似于下面這樣:
cat xxx.log | awk '{t=sprintf("%s %s", $2, $3);}'
還有一個(gè)問題,就是如何將BASH中的開始時(shí)間和結(jié)束時(shí)間變量傳入awk呢?也有辦法的!awk里面有-v選項(xiàng),支持將外部變量傳入其中。那么程序就類似于這樣了:
start_time=`date -d"$last_minutes minutes ago" +"%Y-%m-%d %H:%M:%S"`
end_time=`date +"%Y-%m-%d %H:%M:%S"`
cat xxx.log | awk -v st="$start_time" -v et="$end_time" '{t=sprintf("%s %s", $2, $3); if(t>=st && t<=et){print $0}}'
最后,還有一個(gè)小問題,因?yàn)闀ㄆ谇蟹秩罩荆孕枰紤]臨界時(shí)間點(diǎn)的情況,把當(dāng)前時(shí)間段和上個(gè)時(shí)間段的日志同時(shí)作為輸入即可。
這樣,精確獲取最近一段時(shí)間日志的需求就得到解決了。
--EOF--