如何通過 nginx、php-fpm、PHP 的日誌除錯程式
最近寫了幾篇關於504和502的文章,涉及了很多nginx、php-fpm、php方面的細微知識,這些理論雖然簡單,但對於理解php和http非常重要。熟悉的同學知道,在工作上我主要使用php開發,而開發過程中,除錯是非常關鍵的一個步驟,出現一個問題,快速定位到問題非常關鍵,所以今天簡單區分下nginx、php-fpm、php三者之間的訪問日誌(access.log)錯誤日誌(error.log),理解它們,後續開發的時候會更加順利。
通過下圖,我們能夠了解到這三者之間可能有四種錯誤日誌,如果不理解這張圖的結構,可以看下我以前寫的文章。
在這四個層面(nginx、php-fpm主程序、php-fpm pool工作程序、php)都有與日誌有關的指令,接下去分別描述。
php.ini
每一個php解析器都有一個php.ini,該檔案定義了很多php的預設行為,從日誌的角度看,有三個指令很重要。
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT display_errors=off error_log=/var/log/phperror.log
當display_errors=off的時候,php解析的時候如果出現錯誤(語法錯誤、異常等),則不會在頁面或命令列中不會列印錯誤,在生產環境中,一般將該指令設定為off,否則出現錯誤的時候,訪問者體驗很不好,更嚴重的時候,洩漏了很多隱私資料,比如打印出資料庫的使用者名稱和密碼。
display_errors關閉的時候,我們怎麼知曉php產生了那些錯誤呢?此時可以藉助error_log,該指令指定了一個檔案,如果發生錯誤的時候,就會將錯誤輸出到這個檔案中。
error_reporting則指定了輸出那些型別的錯誤,E_ALL表示輸出所有錯誤,而E_ALL & ~E_DEPRECATED表示除了E_DEPRECATED級別的錯誤不輸出,其他錯誤全部輸出,關於這個指令,也夠我們學一陣子的,但這不是本文的重點。
需要指出的是,如果display_errors=on,一旦產生php錯誤或異常,nginx(包括其他web伺服器)會返回200 http狀態碼,並且在頁面中輸出錯誤;但如果display_errors=off的時候,產生php錯誤或異常的時候,nginx會直接返回500 http錯誤碼。
nginx
對於nginx來說,可以通過以下指令控制訪問日誌和錯誤日誌,非常的簡單:
access_loglogs/access.logmain; error_loglogs/error.log;
對於一個web伺服器來說,nginx通過fastcgi連線後端php-fpm的時候,如果產生fastcgi協議級別的錯誤(比如以前文章中談到的504、502),會記錄在error.log中,比如:
2018/09/19 21:01:19 [error] 17034#0: *253 upstream timed out (110: Connection timed out) while reading response header from upstream, client: *.*.*.*, server: www.simplehttps.com, request: "GET /x.php HTTP/1.1", upstream: "http://*.*.*.*:80/x.php", host: "www.simplehttps.com"
當nginx在內部處理的時候,遇到一些錯誤(比如ssl異常),也會記錄在error.log中,比如:
2018/10/02 10:50:00 [crit] 29363#0: *122 SSL_do_handshake() failed (SSL: error:14209102:SSL routines:tls_early_post_process_client_hello:unsupported protocol) while SSL handshaking, client: 182.132.25.13, server: 0.0.0.0:443
當nginx訪問一個後端不存在的程式時,也會記錄在error.log中,比如:
2018/04/18 21:36:31 [error] 31891#0: *55 open() "/usr/local/index.php" failed (2: No such file or dir ectory), client: 116.62.209.27, server: localhost, request: "GET /index.php HTTP/1.1", host: "",
讀者可能會想,php輸出的錯誤能夠記錄到nginx日誌中嗎,答案是可以的,具體等會說。
php-fpm pool 日誌
在 ofollow,noindex">《什麼是SAPI,FastCGI,PHP-FPM?學習PHP的必備知識》 這篇文章提到php-fpm pool是個非常巧妙的方式,類似於web伺服器的虛擬主機,多個php程序池之間相互隔離,但是php.ini只能有一個,如果某個應用(www.test.com)想開啟php錯誤日誌(即display_errors指令),而另外個應用(www.test.cn)卻想關閉php錯誤日誌,怎麼辦?或者如何在不同的應用之間定義不同的錯誤日誌路徑(即error_log),怎麼辦?
聰明的php-fpm想到了一個辦法,具體的指導策略還是隔離,既然php pool可以分為多個,那麼每個pool可以繼承和覆蓋php.ini的指令,是否可行?
可以的,某些php.ini指令可以覆蓋,但某些指令不可以(全域性的),對於本文要描述的php三個指令,都可以覆蓋過載。
比如 fpm/pool.d/www.test.com.conf 檔案定義如下:
error_log=/var/log/test.com-error.log
而 fpm/pool.d/www.test.cn.conf 檔案定義如下:
error_log=/var/log/test.cn-error.log
好處是什麼?這樣我們能夠清晰的區分不同應用的錯誤,更重要的觀點就是pool繼承了php.ini,每個pool的php配置(php.ini)是獨一無二的。
接下去的是重點,php-fpm pool 還多了一些php.ini沒有的指令,nginx和php-fpm 工作程序(pool)之間通過fastgcgi協議互動,php-fpm為了增加靈活度,增加了一些指令,本文講解的就是 catch_workers_output,檢視該指令的定義:
; Redirect worker stdout and stderr into main error log. If not set, stdout and ; stderr will be redirected to /dev/null according to FastCGI specs. ; Note: on highloaded environement, this can cause some delay in the page
很簡單,該指令預設是關閉的,一旦關閉,php的錯誤輸出會定向到 /dev/null,如果想通過fastcgi協議告知nginx,那麼可以開啟該指令,這樣nginx的access.log就能捕獲到php的輸出(比如php錯誤或異常)。重要的是,該指令一旦開啟,會影響效能。
開啟該指令後,如果遇到php錯誤,nginx的error.log就會記錄,比如:
2018/10/03 13:01:55 [error] 908#0: *4 FastCGI sent in stderr: "PHP message: PHP Parse error:syntax error, unexpected end of file, expecting variable (T_VARIABLE) or ${ (T_DOLLAR_OPEN_CURLY_BRACES) or {$ (T_CURLY_OPEN) in /usr/share/nginx/html/index.php on line 7" while reading response header from upstream, client: 118.207.51.2, server: weiboapi.newyingyong.cn, request: "GET /index.php?ssss HTTP/1.1", upstream: "fastcgi://unix:/run/php/php7.1-fpm.sock:", host: ""
需要注意點是 nginx(user 指令)和php-fpm pool(user指令)的屬主檔案許可權要一致,否則不會產生日誌。
另外php-fpm pool還可以記錄access日誌(access.log)和慢日誌(slowlog),比較簡單就不闡述了。
php-fpm 日誌
剛才說的php-fpm都說的是worker程序,即pool工作程序,php-fpm主程序(root)也可以配置error.log(在php-fpm.conf檔案中配置),就像在 《什麼是SAPI,FastCGI,PHP-FPM?學習PHP的必備知識》 文章說的一樣,我從沒發現該日誌有內容,即使pool配置檔案配置錯誤,也沒有發現日誌有內容,所以暫時可以忽略了,也就是說本文討論了nginx、php-fpm工作程序、php解析器的日誌情況。
相關文章:
我最近寫了一本新書 《深入淺出HTTPS:從原理到實戰》 ,本書github地址是 https://github.com/ywdblog/httpsbook,大家可以一起討論本書。本書豆瓣地址 https://book.douban.com/subject/30250772/(或點選文末“閱讀原文”),如果你讀了本書,還請在豆瓣寫個評論。或者關注我的公眾號 (ID:yudadanwx,虞大膽的嘰嘰喳喳) ,我會分享一些原創文章。