Thinkphp3的monolog實現

一【背景】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支持也就不須要上面那麼複雜了。但此文實現思路仍是值得探討的,但願對有須要的人有幫助

相關文章
相關標籤/搜索