PHP 8.3 の FPM で error_log が無音になる条件を潰す

这篇内容暂时只有 日本語 版本,以下是原文。

PHP 8.3.6、Alpine 3.19 のコンテナ、nginx + php-fpm の構成。FPM のワーカーが 20 前後で回っている決済系のバッチ兼 API で、ある日から 500 が返るのに nginx の error.log にも FPM の error_log にも何も出ない、という状態が半日続いた。アプリ側は set_exception_handler で握ってレスポンスだけ返していたので、ログの欠落に誰も気づいていなかった。データ量は 1 リクエストあたりのペイロードが数百KB、同時実行はピークで 40 程度。

まずやったのは、ログ設定を上から順に確認する作業。php.ini と pool の conf を突き合わせた。

; php.ini
log_errors = On
error_log = /proc/self/fd/2
display_errors = Off
; www.conf
php_admin_value[error_log] = /proc/self/fd/2
php_admin_flag[log_errors] = on
catch_workers_output = yes
decorate_workers_output = no

ここで一点、pool 側に php_admin_value で error_log を書くと、php.ini の設定より優先される。以前の conf には error_log が /var/log/php-fpm/error.log のまま残っていて、コンテナのボリュームにそのディレクトリが無かった。書き込みに失敗しても PHP は黙って捨てるので、エラー自体は起きているのに出力先だけが消える。ディレクトリを作り、ワーカーの実行ユーザーで書けることを確認した時点で、500 のスタックトレースが docker logs に流れ始めた。

なぜこれで読めるようになるかというと、FPM のワーカーの stdout/stderr はマスタ経由でコンテナのログドライバに渡る。catch_workers_output を有効にしないと、ワーカープロセス側の出力は FPM の管理下で切り捨てられる。つまり「ログ設定は正しいのに出ない」の正体は、多くの場合この二段構えの出力先のどこかが欠けている。代償としては、全ワーカーの出力が同じストリームに混ざるので、リクエスト単位の追跡には request_id を自分で付ける必要がある。うちは middleware で uniqid を発行してログの先頭に付けている。

効かない条件もはっきりしている。opcache.preload を使っていると、preload されたファイル内のエラーはワーカー起動前に評価されるので、この設定では拾えない。あと display_errors を On にしていると、レスポンスにエラーが混ざってログ側の挙動が変わる。ステージングで一度これを踏んだ。

同じように「エラーは出ているのにログに無い」を経験した人は、どこを最初に見る?

0
赞同度 100% · 0 条评论 · 6 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。