多彩換新PHP代碼0報(bào)錯(cuò)進(jìn)化之路
寫在前面
PHP在運(yùn)行過程當(dāng)中會(huì)有各種各樣的錯(cuò)誤報(bào)出,有很多卻被忽略了,比如下面幾個(gè):
眾所周知,其實(shí)PHP工程師很容易犯一個(gè)開發(fā)中經(jīng)常會(huì)犯的小毛病,即使用數(shù)組的時(shí)候會(huì)不小心直接使用一個(gè)并不存在的key,如下代碼:
$array = array("a" => "1", "b" => "2");
$use = $array["c"];
其實(shí)很簡單,大家都知道會(huì)報(bào)一個(gè)PHP Notice級(jí)別的錯(cuò)誤,并不會(huì)影響代碼向下執(zhí)行,測試環(huán)境報(bào)在頁面上可能會(huì)不美觀,但你測試的時(shí)候恰好數(shù)據(jù)是全的,沒有看到,到線上又不影響使用,所以很少有人會(huì)關(guān)注。
再舉個(gè)例子:
Warning: Invalid argument supplied for foreach() in
這個(gè)相信大家如果打開php的 error log 也經(jīng)常會(huì)見到,意思也很明顯,就是循環(huán)了一個(gè)錯(cuò)誤的數(shù)組,foreach 的***個(gè)參數(shù)必須是一個(gè)數(shù)組。
當(dāng)然,大家還會(huì)看到諸如Parse error,Fatal error的錯(cuò)誤提示,代碼量小的時(shí)候大部分人都選擇忽略了,但是多彩的小木工還是做了一些小小的工作,把php的 error log 由多變少,***變成了0。先給大家看一下我們的成果:

可以看到開始我們的 error log 有21M之多,***變成了現(xiàn)在的沒有了!
雖然有一些bug改起來還是比較棘手的,但大部分其實(shí)代碼作者看到就明白是什么問題了,但是如果他從不知道,也沒有系統(tǒng)的看到,就永遠(yuǎn)可能不會(huì)去修改了。這時(shí)候一個(gè)能自動(dòng)分析log并把相關(guān)問題發(fā)送給對應(yīng)責(zé)任人的腳本就呼之欲出了。
寫在中間(腳本設(shè)計(jì))
首先由于這本身定義為一個(gè)分析日志的腳本,怕是log文件過大分析的時(shí)候占用生產(chǎn)機(jī)資源,因?yàn)槲覀儽旧砭陀猩a(chǎn)機(jī)使用消息隊(duì)列機(jī)制同步日志到開發(fā)機(jī)的現(xiàn)成流程,所以就直接用了。
- php_error_log 同步到非生產(chǎn)機(jī)
可以和大家分享的是多彩使用了nsq 。不斷的把生產(chǎn)機(jī)log同步到非生產(chǎn)機(jī)。這里呢是一位瓦工小哥使用go弄了一個(gè)小工具來實(shí)現(xiàn)tail的功能來直接把log不斷發(fā)送到消息中心。***寫到非生產(chǎn)機(jī)以供分析。
- 分析 log 文件的內(nèi)容
好,log文件有了,下面開始寫腳本,linux有個(gè)awk命令灰常好用,能順利完成我們的需求,所以我們直接選用shell腳本來搞。
大家可以對比自己的error_log修改相關(guān)細(xì)節(jié)。
先寫一個(gè)awk命令:
awk -v today="31-Jul-2017" -FAsia/Chongqing] '{if($0~today) print $2}' 20170731.log | sort | uniq -c | sort -k1nr | grep 'on line'
我們用時(shí)區(qū)切割將真正需要的當(dāng)天錯(cuò)誤詳情拿出來,排序。
這里面有一個(gè)需要說明的地方,為什么在uniq之前要先sort,因?yàn)閷?shí)測uniq的統(tǒng)計(jì),是在相鄰的行一樣的時(shí)候才能夠合并計(jì)數(shù),所以我們先排序使得相同的錯(cuò)誤能夠在相鄰的行。***你得到的結(jié)果應(yīng)該是這樣的:
960 PHP Notice: some notice in /some/path/notice.php on line 119
850 PHP Warning: some Warning in /some/path/warning.php on line 110
***列是錯(cuò)誤發(fā)生的次數(shù),后面分別為錯(cuò)誤詳情以及文件包括代碼所在的行。
到這里其實(shí)已經(jīng)可以完成一部分需求了,只是需要大家自行認(rèn)領(lǐng)相關(guān)問題,因?yàn)槎嗖适褂昧薵it作為倉庫,我們還可以做的更好。
- 使用 git blame 獲取作者和相關(guān)代碼詳情
關(guān)鍵代碼:
code_writer=$(git blame -L$file_line,$file_line -e $file_name | awk 'BEGIN{FS=")"} {print $1}' | awk -F '[<>]' '{print $2}')
code_content=$(echo $(git blame -L$file_line,$file_line -e $file_name | awk 'BEGIN{FS=")"} {print $2}') | sed 's/^ //g;s/ $//g')
解釋一下,當(dāng)你使用git blame -e 命令的時(shí)候,可以得到如下類似的結(jié)果:
d8eabd28c (<mumulaonian@gmail.com> 2017-07-12 16:58:38 +0800 4) * // some code by akmumu
-L 參數(shù)是查看指定行的結(jié)果,如代碼所示,再使用awk和sed可以獲得作者郵箱和代碼詳情。
其實(shí)到這里為止你已經(jīng)可以搞一個(gè)循環(huán)生成一個(gè)包含table的html源碼,然后通過郵件服務(wù)器發(fā)給相關(guān)的人了,作者看到,大概很快就能搞掉一半的報(bào)錯(cuò)了,希望幾天之后你們也可以做到 error_log 文件 0KB。但如果你們也恰好使用自己搭建的gitlab管理代碼,那么其實(shí)還可以做的更多。
- 使用gitlab的相關(guān)Api完成一些自動(dòng)化的事情
其實(shí)gitlab提供給了我們很多的Api,你可以用之來做很多的事情,可以自由選擇一些使用,比如我們有使用issues相關(guān)的Api,直接創(chuàng)建issue,文檔:issues文檔。
你如果也使用了 capistrano 和 gitlab CI 作為自動(dòng)化部署工具,那么你還可以在完成修復(fù)issue的 merge request 之后將當(dāng)天的相關(guān)報(bào)錯(cuò)log通過shell刪掉,這樣明天的通知在確實(shí)不存在本bug的情況下將不會(huì)存在相應(yīng)log,這里很簡單,還用 sed 即可。
當(dāng)然CI的腳本也有很多想象空間,大家自由發(fā)揮好了。
寫在后面
我們在解決以上報(bào)錯(cuò)的時(shí)候解決了很多意料之外的問題,一些不被關(guān)注的問題,還有某個(gè)服務(wù)的一個(gè)不常用的小部分其實(shí)已經(jīng)有兩天不可用的情況了,幸好比較及時(shí),沒有發(fā)生太多的損失。所以說此log是急需被關(guān)注的,如果你還沒有做,就抓緊開始吧,不要應(yīng)驗(yàn)這句話,”那些年里看到了,懂了,卻不做“。