例外処理とロギングのひな形

例外をキャッチしたときにしたいこと

  • ログ取り
  • エラー画面の表示

なので、Exceptionクラスを拡張して、上の2つのメソッドを追加する。
・exception.php

<?php
/* */
class My_Exception extends Exception {
  protected $error_message;
  protected $template = "errors/500.phtml";
  
  public function __construct($message, $code=0) {
    parent::__construct($message, $code);
    //
    $this->logging();
    $this->display();
  }
  
  public function logging() {
    $this->error_message = $this->createMessage();
    Logger_Observer::notify("FATAL", $this->error_message);
  }
  
  public function createMessage() {
    return $this->getMessage() . ", StackTrace: " . $this->getTraceAsString();
  }
  
  public function display() {
    $view = new View;
    if(Config::read("main", "debug")) {
      $view->assign("error_message", $this->error_message);
    }
    $view->render($this->template);
  }
}

404 Not Foundな処理をしたいときは、この例外クラスを継承してテンプレートを404エラー用のものに変更すれば大丈夫。
ん?まあいっか。
Configとかは作ってるフレームワークの設定クラスなのであまり気にしない。


ロギングは例外以外でも使いたいので、別のクラスにして実行する。
・logger.php

<?php
/* */
class Logger {
  protected $message;
  
  public function __construct($type, $message) {
    $this->message = $this->format($type, $message);
  }
  
  protected function format($type, $message) {
    $str = sprintf("%s [%s] %s", date("Y/m/d H:i:s"), $type, $message);
    return $str;
  }
  public function getMessage() {
    return $this->message;
  }
}

final class File_Logger extends Logger {
  
  public function __construct($type, $message) {
    parent::__construct($type, $message);
    
    $this->log();
  }
  private function log() {
    error_log($this->message, 3, APP . DS . "logs/error.log");
  }
  public static function raise($type, $message) {
    return new self($type, $message);
  }
}

final class Mail_Logger extends Logger {
  // メール処理を書く

  public static function raise($type, $message) {
    return new self($type, $message);
  }
}

// log observer
class Logger_Observer {
  public function __construct() {}
  
  public static function run($type, $message) {
    $log_conf = Config::read("log");
    if($log_conf["level"]) {
      if($log_conf["file"]) File_Logger::raise($type, $message);
      if($log_conf["mail"]) Mail_Logger::raise($type, $message);
    }
  }
}

とりあえず、実行日時、エラーの箇所、エラーメッセージ、スタックトレース辺りを記録できればいいかなと思ったので、そういう感じにしてみました。


ファイルにログを取るときは、PHPの場合error_log関数で一発なので楽ですね。
ロギングの実行関数raiseを親クラス側で定義できればいいのだけど、継承したクラスでstaticな関数を実行すると、なぜか実行しているクラス名が親クラスになる。
これってどうしようもない問題なのかな?
そうだとすると、ロギングの子クラスでは全部宣言しないと駄目だ。。。


使いたいときは以下のように例外をキャッチして使う。

<?php
function test() {
  throw new My_Exception("Raise My Exception. This is the message!'");
}

try {
  test();
} catch(My_Exception $e) {
  die();
}

ログにはこんな感じで出力されます。

2009/02/15 22:30:04 [FATAL] Raise My Exception. This is the message!
#0 /Users/cheesepie/Sites/test/exception_test.php(7): test() #1 {main}