前回は簡単な使い方を書きましたが、今回は拡張する人用の情報を書きます。

クラスの構成

こんな感じになっています

┌─────────────------------------─┐   ┌────------------------------──────────-─┐┌─---------------────────┐
│ Wads_Log_RotationFile_Abstract │◇┬→│ Wads_Log_RotationFile_Writer_Interface ││Zend_Log_Writer_Abstract│
└───────-------------------──────┘ │ ├───────------------------------─────────┤└──-------------─────────┘
                ▲                  │ │ + update()                             │            ▲
                │                  │ └─────────────---------------------──────┘            │
     ┌────────-----------───┐      │                     △                                 │
     │Wads_Log_RotationFile │      │                     ├─────────────────────────────────┘
     └─-----------──────────┘      │        ┌────────────────────────────┐
                                   │        │Wads_Log_Writer_RotationFile│
                                   │        ├────────────────────────────┤
                                   │        │ + update()                 │
                                   │        └────────────────────────────┘
                                   │ ┌──────────────────────────────────────┐
                                   └→│Wads_Log_RotationFile_Policy_Abstract │
                                     ├──────────────────────────────────────┤
                                     │ + trigger()                          │
                                     │ + rollOver()                         │
                                     └──────────────────────────────────────┘
                                                          ▲
                                     ┌────────────────────┴───────────────────┐
                    ┌──────────────────────────────────────┐┌──────────────────────────────────┐
                    │Wads_Log_RotationFile_Policy_Datetime ││Wads_Log_RotationFile_Policy_Size │
                    ├──────────────────────────────────────┤├──────────────────────────────────┤
                    │ + trigger()                          ││ + trigger()                      │
                    │ + rollOver()                         ││ + rollOver()                     │
                    └──────────────────────────────────────┘└──────────────────────────────────┘



デザインパターンでいうと、ObserverとAdapterパターンあたりを意識しています。 PHPによるデザインパターン入門の本が役に立ちました。

コンポーネントは、大きく分けると、

  • Wads_Log_Writer_RotationFileがZend_Logのライター
  • Wads_Log_RotationFileがローテーションの管理を行う
  • Wads_Log_RotationFile_Policy_Abstractの継承クラスにローテーションの方法が既定されている

の3つに分けられます。

Wads_Log_Writer_RotationFile

Wads_Log_Writer_RotationFileがZend_Log_Writer_Abstractを継承していて、Zend_Logでいうところのライターになります。

さらに、Wads_Log_RotationFile_Writer_Interfaceも実装していて、これでログローテーションに対応させています。

このWads_Log_RotationFile_Writer_Interfaceにはupdate()というメソッドが定義されています。

これはWads_Log_RotationFileでログのローテーションが行われたときに呼び出されるコールバック関数です。update()関数には、引数で渡される新しいログファイル名を用いて、再度ログファイルをオープンする処理を記述します。

いくつか関数が追加されていますが、基本的な部分はZend_Log_Writer_Streamと同じ内容となっています。_write()関数でWads_Log_RotationFileからログファイルのローテーションを行うrollOver()関数を呼び出している処理の部分が異なるとことです。

protected function \_write($event) {
    // do rotation file
    if($this->\_rotationClass !== null) {
        try {
            $this->\_rotationClass->rollOver($this);
        } catch (Wads\_Log\_Exception $e) {
            $this->shutdown();
            throw $e;
        }
    }

    // write log message
    $line = $this->\_formatter->format($event);

    if(false === @fwrite($this->\_fp, $line)) {
        require\_once 'Wads/Log/Writer/Exception.php';
        throw new Zend\_Log\_Exception('Unable to write to file');
    }
}

もし、ローテーションに対応した独自のライターを作りたい場合は、Zend_Log_Writer_Abstractを継承して、Wads_Log_RotationFile_Writer_Interfaceを実装すればOKです。

Wads_Log_RotationFile

Wads_Log_RotationFileでログのローテーションを管理します。

Wads_Log_RotationFileではWads_Log_RotationFile_Policy_Abstractを継承したポリシークラスを内部に持っています。このオブジェクトを使用して、ログのローテーションが必要かどうかのタイミングを問い合わせ、必要に応じてローテーションを指示します。

具体的には、ライターからrollOver()関数が呼び出されると、ポリシークラスで既定されているtrigger()関数を呼び出してログのローテーションが必要かどうかを問い合わせます。

ローテーションを行う場合は、ポリシークラスのrollOver()関数を呼び出してログのローテーションを行います。

rollOverの戻り値として新しいファイル名が返されるので、これを先に示したコールバック関数update()の引数として、ライターに渡します。

public function rollOver(Wads\_Log\_RotationFile\_Writer\_Interface $writer) {
    if($this->\_rollOver()) {
        $writer->update($this->\_logFile);
    }
}

protected function \_rollOver() {
    if(!$this->\_rotationPolicyClass->trigger()) {
        return false;
    }

    $this->\_logFile = $this->\_rotationPolicyClass->rollOver();

    return true;
}

ポリシークラスでは、新しいログファイル名を返すのみで、ログファイルのオープンなどは行いません。これらの処理はライターが行います。

このクラスは独自の拡張を行うようなことはあまりしないと思います。

ポリシークラス

ログのローテーションのタイミングやどのように行うかは、このクラスが担当します。

ポリシークラスはWads_Log_RotationFile_Policy_Abstractという抽象クラスを継承する必要があります。

この抽象クラスには、trigger()とrollover()というabstract関数が定義されているので、これをサブクラスで実装します。

/**
 * @return boolean
 */
abstract public function trigger();

/**
 * @return string
 */
abstract public function rollOver();

trigger()では、現在のログファイル($_fileName プロパティ)がログローテーションを行うタイミングかどうかを判定して結果を返します。ローテーションを行う場合はtrureを、行わない場合はfalseを返します。

rollOver()関数では、ログファイルのローテーションを行い、新しいログファイル名を返します。

その際、既存ログファイルの管理(リネームや削除)は、このクラスで行いますが、新しいログファイルの作成、オープンはここではなくライター側で行います。

このほか、Wads_Log_RotationFile_Policy_Abstractには、現在のログファイル名を設定・取得するsetFileName()とgetFileName()関数があります。

ひとまずWads_Log_RotationFile_Policy_Abstractを継承して、trigger()関数とrollOver()関数を実装すれはポリシークラスとして動作させることができます。