CodeIgniter3で本番環境時にエラーが発生した時にメールを送るようにする

今回は、CodeIgniter3で本番環境時にエラーが発生した時に、自身のメールアドレスに下記のような内容のメールを送れるようにしたので、やり方を紹介していく。

DATE: 2020/02/26 17:29:55
MESSAGE: syntax error, unexpected '$data' (T_VARIABLE)
FILEPATH: /var/www/html/application/controllers/AdminController.php
LINE: 22

環境

  • CodeIgniter 3.1.6
  • PHP 7.4.1
  • Apache 2.4.6 (本番)
  • CentOS 7 (本番)

エラー発生時にメールを送るようにする方法

エラー発生時にメールを送れるようにするために、今回は、CodeIginterのCore部分で定義されているset_error_handlerset_exception_handlerを上書きする方法を採用した。

ただCore部分をいじるのは不味いので、Hooks(フック――フレームワークコアの拡張 — CodeIgniter 3.2.0-dev ドキュメント)を使うことで、エラーハンドリングを変えるようにする。

実装方法

1,Hooksクラスを作成する

まずはapplication/hooksディレクトリ内にErrorHandler.phpを作成して、そのファイルに下記のコードを書く。

<?php

class ErrorHandler
{

    public function set_error()
    {
        set_error_handler([$this, "_error_handler"]);
        set_exception_handler([$this, "_exception_handler"]);
    }

    public function _error_handler($severity, $message, $filepath, $line)
    {
        if (((E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity)
            $this->send_email($message, $filepath, $line);

        // CodeIgniterのCore部分のerror_handlerをそのまま使う。
        _error_handler($severity, $message, $filepath, $line);
    }

    public function _exception_handler($exception)
    {
        $this->send_email(
            $exception->getMessage(),
            $exception->getFile(),
            $exception->getLine()
        );

        // CodeIgniterのCore部分の_exception_handlerをそのまま使う。
        _exception_handler($exception);
    }

    public function send_email($message, $filepath, $line)
    {
        $subject = "【重要】エラー検知通知メール";

        $date = date('Y/m/d H:i:s');

        ob_start();
        include VIEWPATH . "/errors/error_mail.php";
        $mailbody = ob_get_contents();
        ob_end_clean();

        $from = "From:" . getenv("CI_EMAIL_SENDER");
        mb_send_mail(getenv("CI_EMAIL_RECEIVER"), $subject, $mailbody, $from);
    }
}

今回はメールを送る処理の後に、CodeIgniterのCore部分のエラーハンドリング関数を使うようにした。

当初はErrorHandlerクラスのメソッドは全てstaticにしようとしたけど、「CodeIgniter/Hooks.php at develop · bcit-ci/CodeIgniter」のHook処理を見てみると、一旦クラスをインスタンス化してメソッドを呼び出しているので、Hookクラスにstaticは使えないことが分かったので見送った。

2,エラーメールのviewを作成

今回はエラーメールのviewをapplication/views/errors/error_mail.phpを作成した。

<?php

echo "DATE: " . $date . "\n";
echo "MESSAGE: " . $message . "\n";
echo "FILEPATH: " . $filepath . "\n";
echo "LINE: " . $line . "\n";

もっと良い処理がありそうだけど、今回はこれでいく。

3, Hookの設定をする

application/config/config.phpに行き、以下の設定をTRUEに変更し、Hook処理ができるようにする。

$config['enable_hooks'] = TRUE;

そしてappplication/config/hooks.phpに行き、以下のコードを追加する。

<?php

$hook['pre_system'] = array(
    'class'    => 'ErrorHandler',
    'function' => 'set_error',
    'filename' => 'ErrorHandler.php',
    'filepath' => 'hooks',
);

今回はpre_systemにHookを追加した。pre_systemにHookを追加することで、ルーティングが行われる前にHook処理を行ってくれる。

pre_system システム実行中の非常に早い段階で呼び出されます。ベンチマーククラスと フッククラスだけがこの時点でロードされています。ルーティングや 他のプロセスは実行されていません。 参考: フック――フレームワークコアの拡張 — CodeIgniter 3.2.0-dev ドキュメント

filepathをhooksとすることで、application/hooksディレクトリ内にあるErrorHandler.phpを読み込むようになり、Hook処理時に下記のような処理を行ってくれるようになる。

$hook = new ErrorHandler();
$hook->set_error();

参考:フック――フレームワークコアの拡張 — CodeIgniter 3.2.0-dev ドキュメント