WordPressのエラー監視とデバッグの概念図。エラー送信停止、error_get_last()の上書き、Fatalエラー捕捉、WP-CLIによる疎通確認といった点検手順を、開発者がタブレットを使いデバッグする様子が描かれています。

はじめに

WordPress から外部のログ集約サービスへエラーを送る仕組みを運用していると、「ある日を境に記録が止まった」という場面に出会います。
このとき最初に疑いたくなるのは送信側の故障ですが、実際に点検してみると、原因は別のところにありました。

実運用しているサービスで点検したところ、「送る条件に当たる事象そのものが止まった」可能性が高いと分かりました。
一方で、点検の過程で Fatal を取りこぼす本物の欠陥も見つかっています。
この記事では、その点検の型と、見つかった欠陥の直し方、本番で安全に疎通確認する WP-CLI コマンドの作り方をまとめます。

こんな人におすすめ

  • WordPress のエラーを外部サービスへ送る仕組みを自作・運用している方
  • register_shutdown_function と error_get_last() で Fatal を拾っている方
  • 「通知が止まった」ときの切り分けの順番を整理したい方
  • 本番で安全に使える点検用の WP-CLI コマンドを作りたい方

「止まった」は「壊れた」とは限らない

記録が止まったとき、仮説を立てる順番で調査の速さが変わります。
目安として、次の順で確認すると早く絞り込めました。

  1. 送信条件を一覧にする(例:Fatal、特定の名前空間の 5xx)
  2. 受け手側の集計を見て、実際に届いていた記録の種類を確認する
  3. 届いていた記録が 1 種類のエラーだけなら、その事象が消えた可能性を先に疑う

今回は、届いていた記録が 1 種類のエラーだけでした。
「送信の故障」より先に「送る対象の事象が消えた」を疑うと、無駄な調査を減らせます。

ただし、コードを突き合わせる過程で、送信側にも別の欠陥が見つかりました。次の節で説明します。

error_get_last() は後続のエラーで上書きされる

register_shutdown_function で登録した関数は、WordPress の shutdown アクションや他プラグインの shutdown 処理より後に動きます。
その間に Notice が 1 つ出るだけで、error_get_last() の中身は Notice に置き換わり、Fatal の情報が消えます。
WP_DEBUG が無効の環境ではログにも残らないため、気づくのが難しい欠陥です。

対策は、取れる一番早い時点で Fatal を控えておくことです。
shutdown アクションを最優先で実行し、wp_php_error_args フィルターでも控えます。

// 自分の shutdown 関数は、他の shutdown 処理より後に動く。
// 非致命エラーで error_get_last() が上書きされるので、Fatal は早い時点で控える。
\add_action( 'shutdown', [ __CLASS__, 'capture_last_error' ], PHP_INT_MIN );
\add_filter( 'wp_php_error_args', [ __CLASS__, 'on_php_error_args' ], PHP_INT_MIN, 2 );

public static function capture_fatal( ?array $error ): void {
	if ( null === $error || null !== self::$captured_fatal ) {
		return; // 最初の1件を優先し、後から上書きしない
	}
	if ( 0 === ( ( $error['type'] ?? 0 ) & self::FATAL_MASK ) ) {
		return;
	}
	self::$captured_fatal = $error;
}

ポイントは 2 つあります。

  • 致命的エラーの型判定を capture_fatal の 1 か所にまとめ、フック経由でも shutdown 経由でも同じ経路で控える
  • 最初の 1 件を優先し、後から置き換えない

失敗ログには例外メッセージを出さない

送信に失敗したときのログにも注意が必要です。
getMessage() や WP_Error の全文には、URL などが混ざることがあります。
ログに秘匿したい情報が漏れないよう、例外は型(クラス名)だけを残します。

} catch ( \Throwable $e ) {
	// メッセージには URL 等が入りうるので、型だけを残す
	error_log( '[Reporter] on_shutdown failed: ' . get_class( $e ) );
}

WP_Error の場合も、エラーコードと cURL 番号だけに絞ると安全です。

受け手のスキーマ検査を送信側のテストに写す

点検では、受け手のスキーマ検査で弾かれる payload の欠陥も見つかりました。
たとえば request.url が空だと、相手側が 400 を返す、といった条件です。

こうした欠陥は、送信側だけをテストしていても見つかりません。
相手のスキーマを PHP のテストヘルパに写し、payload が通ることをテストで固定しておくと、再発を防げます。
写し元のコミット SHA をコメントに明記しておくと、相手側の変更に追従するときに差分を追いやすくなります。

本番で安全に使える点検用 WP-CLI コマンド

運用者が本番で疎通確認できるよう、status と test の 2 つの WP-CLI コマンドを用意しました。
設計で意識したのは、「出すものを絞る」ことです。

  • 出す:ホスト名、設定の有無(yes/no)、ステータス、応答コード
  • 出さない:URL 全体、秘密鍵、応答本文

本番へ実送信する test には、次の安全策を入れています。

  • 確認プロンプトを付ける
  • ERROR 形の送信は --dry-run なしでは拒否する(Issue の起票や通知が走るため)
# 設定の状態を確認する(送信はしない)
wp <command> log-hub status

# 疎通確認(ERROR 形は --dry-run が必須)
wp <command> log-hub test --dry-run

<command> の部分は、お使いのプラグインの名前空間に置き換えてください。

つまづきやすいポイント

  • status は CLI の環境しか見ていません。 wp_get_environment_type() は定数より先に環境変数を読むため、Web と CLI で本番判定が食い違うことがあります。定数・getenv・SAPI のどこから判定したかも表示すると、読み違いを防げます。
  • 副作用のある修正は、挙動の変化を明記しましょう。 今まで捨てられていた Fatal が届くようになると、通知や起票が増えます。PR にその旨を書いておくと、運用側が驚きません。
  • 1 回の失敗で、直接送信とフック経由の 2 件が送られることがあります。 重複は別途解消が必要です。
  • 停止期間中に本当にエラーが起きていなかったかは、別の情報源で確認しましょう。 Web サーバや CDN のログとの突き合わせが有効です。

まとめ

「記録が止まった」ときは、送信の故障を疑う前に、送信条件の一覧と受け手の集計を見るのが近道でした。
一方で、error_get_last() が後続の Notice で上書きされる欠陥は、shutdown アクションの最優先実行と wp_php_error_args で早めに控えることで防げます。
受け手のスキーマはテストに写し、失敗ログには型だけを出し、点検用 CLI は出す情報を絞る。
この 3 点を押さえておくと、次に記録が止まったときの点検がかなり楽になります。