一【背景】php
公司有個基於tp3的項目,此項目經歷屢次改版重構,有種千瘡百孔的感受,讓人看着隱隱心疼,特別是在日誌模塊上,基於tp提供的日誌功能,各類混亂不協調,且與業務的耦合很是高;html
可是這個項目又是很重要的,是最直接面向用戶的入口,不出現問題則好,一出現就是一團亂,問題定位均需依賴後端接口或者第三方,也所以背過很多的鍋,眼淚老是默默的流在內心;laravel
是時候作出改變了!git
二【選定方案】github
項目基於Thinkphp3.2版本,此版本在日誌方面使用的是php內置的error_log函數實現,在不改動tp內核源碼狀況,很難定製本身的日誌驅動,咱們使用過程存在幾個埋怨點:web
1)日誌格式難統一,沒有一個標準何時寫日誌,寫什麼日誌等ajax
2)日誌擴展困難,特別對於項目中特定的須要log的狀況thinkphp
3)log代碼與業務耦合厲害,維護起來很讓人煩躁數據庫
那麼基於tp3自己的日誌作些修補,意義不是太大,治標不治本,因此咱們努力尋找第三方完整的開源日誌庫:json
1)這裏咱們應該首先就會想到php4log,一個很強大,很完整的日誌庫,但使用過程老是感受結構太複雜(或者沒有深入體會到做者的精髓吧);
2)性能方面國產的seaslog無疑是最佳的,不論從官方測試數據仍是本地的驗證,也都證明seaslog是個硬貨,但有幾個痛點讓咱們放棄了它:1-須要安裝擴展+添加配置;2-日誌格式被限定太不自由3-擴展困難;
3)Monolog,功能強大,可支持把日誌發送到文件、socket、郵箱、數據庫、各類webservices等(雖然咱們就只用到文件);遵循PSR3日誌接口規範,可容易與其餘日誌類庫替換;使用composer維護相關依賴,遵循PSR4規範,自動加載;擴展性良好,可輕鬆定製各類格式;使用便利,這個很重要;包括symfony、laravel、cakephp框架均內置monolog;另外,咱們有個基於lume框架的系統(此框架內置了monolog)已穩定使用1年多,且天天日誌量再千萬級,輕鬆支撐;
基於對monolog的瞭解,咱們就選擇了monolog
三【方案實現】
這是一個不斷挖坑,並埋坑的過程,痛並快樂
Tp3日誌模塊是直接耦合在覈心源碼裏,特別是錯誤或異常處理,框架自己沒有提供入口給你去從新實現或擴展日誌功能(tp5添加了日誌的擴展口,經過鉤子的方式,可憐的是tp3到tp5的升級是不可能平滑的,看到這裏真是有一萬隻草泥馬飄過)
這裏有個很重要的前提:不修改tp自己的源碼,不用問爲何
下面咱們開始說明實現流程:
一、Composer安裝monolog
#composer requrie snowair/think-monolog:dev-master
這裏我找了個對monolog針對tp3的一層封裝,github以下:(其實只是作了一層monolog的啓動操做)
https://github.com/snowair/think-monolog
注意:composer安裝是一項苦力活,特別是在國內神奇的網絡下,那麼有解決麼,偉大的開源鬥士爲你準備了下面代碼:
找到根目錄composer.json,添加國內源,其實仍是慢的,這時候須要耐心、耐性、耐操
另外,記得添加下面的autoload,這是一個神奇的東西,它會自動生成一個autoload.php文件,徹底支持PSR4規範,後面咱們就要用到它
{
"name": "test",
"require": {
"snowair/think-monolog": "dev-master"
},
"autoload": {
"autoload": {
"psr-4": {"App\\": "app/"}
}
},
"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.phpcomposer.com"
}
}
}
裝好後,根目錄下就有vendor,完美擁有monolog。革命成功走出第一步
二、將monolog添加到框架自動加載
根目錄index.php,添加上面自動加載文件autoload.php
此操做,可實現後面調用monolog過程可直接引入,如
三、首先咱們要接管tp自己的錯誤處理器
研究了下tp核心代碼,他的錯誤處理是直接在代碼裏寫死的,在Think.class.php下:
這也是咱們很難去擴展他的緣由,因此咱們第一想法就是直接改源碼,這太不和諧了。通過一晚上的冥思,想到了一個絕妙的方案:
1)入口文件index.php添加了一個異常處理類,因此異常可方便的可一個類中處理掉
感興趣的我這能夠提供此類
2)異常類中接管register_shutdown_function
注意,此處不能接管set_error_handler、set_exception_handler,緣由跟他們3個的機制有關,這個我後面另外詳細說明:
3)添加tp的action_begin行爲來接管set_error_handler、set_exception_handler
到此tp的錯誤及異常狀況均已接管到異常類下,咱們能夠自由實現異常邏輯,包括寫log
4)通用的錯誤頁面
支持了pc和h5端通用的錯誤頁,在出現致命錯誤時記錄log的同時也會重定向到錯誤頁的展現,以便在用戶出錯狀況下,能夠提供給咱們查下bug的依據
錯誤頁效果:其中error code爲每次請求的位置requestid,可經過日誌系統惟必定位到
四、統一訪問log的實現
1)一樣用到tp的行爲,添加2個行爲,分別對應action_beigin和app_end
即在php進程的開始和結束咱們作行爲操做,行爲其實就相似鉤子,執行到某個點時會自動觸發
2)明肯定義了日誌的格式,字段等,保證全部訪問都可記錄詳細log,且不須要修改業務代碼
3)爲了更好的使用日誌,咱們定義了幾個可變化的字段,可在業務過程當中使用:
elk_mor_info:須要額外記錄的log均放置在此字段下,使用例子以下
record($msg, $key = '', $type = 'elk')
$msg-內容,json格式
$key-二級json格式字段名,幾個通用定義
curl_request curl請求第三方參數
curl_response curl請求返回結果
curl_time curl請求處理時間
curl_getinfo curl請求info信息
error_sys 系統錯誤
Info_xxxx 其餘信息類型
$type-需定義爲’elk_more_info’
你們需嚴格按照定義的規則進行記錄,不然容易致使日誌格式混亂
response:tp3沒找到捕捉最終返回的content值,這裏使用的方法是,在controller的基類basecontroller裏設置統一的返回格式:
在此處作response的收集,這裏收集是針對ajax,對於面向用戶訪問的項目,不少是直接訪問html頁面,這時候實際上是沒有response這個東西。
另外,若是沒有調用到通用返回方法,但又想添加response值,也能夠相似elk_more_info方式進行設置收集
五、須要注意的點
1)業務開發過程,儘可能不要使用exit()、die()來結束,由於這2個方法都是直接終止腳本執行,表示結束一個php進程,以後的代碼將都不會被執行,包括行爲裏面的函數或者各類鉤子函數都不會被觸發到。
此狀況,通常狀況都可使用return來代替,不過在業務邏輯上就須要作相應處理
2)若是非要用exit或die時,又要記錄log,這裏也提供了在exit以前強制寫log的函數供調用:
Logger::foreWrite() -- 無需帶參數,它的做用是將內存中的log及當前須要記錄的信息強制寫入的文件中
3)爲了更好控制log,添加了幾個log相關的配置,配置文件在每一個模塊下,即只對相應模塊有效,如Event模塊/Apps/Event/Conf/config.php
在實際環境中,可根據業務須要自行調整
六、日誌的後續
日誌存放位置:/home/www/log/xxxx/
並根據模塊劃分如Event/2017-07-21.log,天天每一個模塊一份日誌
公司已有完善的log監控系統,基於ELK作的二次開發,添加log採集程序便可完成log系統接入,這時候查log就各類方便:
今後媽媽不再擔憂查不到bug了
四【最後的話】
以上是基於thinkphp3上monolog實現的詳細思路,你們若是有更好的方案歡迎探討。
另外,注意下,最終咱們log是有落地到log監控系統的,你們在實際項目中須要評估好是否有這樣條件,以避免不能知足實際要求
咱們項目中也有用到thinkphp5,它就是一個徹底跟隨laravel思想的國貨,已經能很好的擴展本身的日誌系統,包括擴展monolog支持也就不須要上面那麼複雜了。但此文實現思路仍是值得探討的,但願對有須要的人有幫助