ロギング

Yii には柔軟で拡張可能なログ機能が備わっています。 記録されたメッセージはログレベルとメッセージカテゴリによって分類可能です。 レベルとカテゴリフィルターを使うことで、特定のメッセージをファイルやメール、ブラウザといった別の送り先に送信することも可能です。

1. メッセージをログに記録する

メッセージは Yii::logYii::traceのどちらかを呼ぶことでログに記録可能です。 この二つのメソッドの違いは、後者はアプリケーションが デバッグモード で動いているときだけメッセージを記録するということです。

Yii::log($message, $level, $category);
Yii::trace($message, $category);

メッセージをログに記録するとき、カテゴリとレベルを指定する必要があります。 カテゴリは xxx.yyy.zzz という パスエイリアス と似たフォーマットの文字列です。 例えば、CController でメッセージがログに記録されるとすると、system.web.CController というカテゴリを使うことができるでしょう。 メッセージレベルは以下の値のいずれかになります。

  • trace: Yii::trace によって利用されるレベルです。 開発中に、アプリケーションの実行フローをトレースするためのものです。

  • info: 一般的な情報を記録するためのレベルです。

  • profile: 後ほど述べるパフォーマンスプロファイルのためのレベルです。

  • warning: 警告メッセージのためのレベルです。

  • error: 致命的なエラーメッセージのためのレベルです。

2. メッセージルーティング

Yii::logYii::trace によって記録されたメッセージはメモリに一時的に保持されます。 多くの場合、メッセージをブラウザに表示したり、ファイルやメールといった持続的ストレージに保存する必要があります。 これを メッセージルーティング と呼びます。つまり、メッセージを異なった送り先に送信するという意味です。

Yii におけるメッセージルーティングは、CLogRouter アプリケーションコンポーネントによって制御されます。 このコンポーネントは、いわゆる ログルート のセットを管理します。 各ログルートは一つの送信先を表します。 メッセージはログルートに沿って送信され、レベルとカテゴリによってフィルタすることが可能です。

メッセージルーティングを使うには、CLogRouter アプリケーションコンポーネントをインストールし、プリロードする必要があります。 また、コンポーネントの routes プロパティに必要なログルートを設定する必要があります。 以下に、必要となる アプリケーション初期構成 の例を提示します。

array(
    ......
    'preload'=>array('log'),
    'components'=>array(
        ......
        'log'=>array(
            'class'=>'CLogRouter',
            'routes'=>array(
                array(
                    'class'=>'CFileLogRoute',
                    'levels'=>'trace, info',
                    'categories'=>'system.*',
                ),
                array(
                    'class'=>'CEmailLogRoute',
                    'levels'=>'error, warning',
                    'emails'=>'admin@example.com',
                ),
            ),
        ),
    ),
)

この例では二つのログルートを設定しています。 最初のルートは CFileLogRoute で、これはメッセージを runtime ディレクトリ以下のファイルに保存します。 このルートで保存されるのは、レベルが traceinfo で、かつカテゴリが system. で始まるものだけです。 二番目のルートは CEmailLogRoute で、指定されたメールアドレス宛にメッセージを送信します。 このルートでは、レベルが errorwarning のものだけが送信されます。

さらに高度な例として、特定のカテゴリがログに出現するのを抑止する方法を以下に示します ( Yii 1.1.13 以降で利用可能になった except プロパティを使います )。

'routes'=>array(
        array(
            'class'=>'CEmailLogRoute',
            'levels'=>'error, warning',
            'except'=>'system.CModule.*' // CModule のログ以外をすべてメールで送信
            'emails'=>'admin@example.com',
        ),
        array(
            'class'=>'CWebLogRoute',
            'categories'=>'system.db.*',
            'except'=>'system.db.ar.*', // すべてのレベルの db ログを表示 (ただし ar カテゴリは除外)
        ),

Yii では以下のログルートが利用可能です。

  • CDbLogRoute: メッセージをデータベースに保存する
  • CEmailLogRoute: メッセージを指定されたメールアドレスに送信する
  • CFileLogRoute: メッセージをアプリケーションの runtime ディレクトリ以下にファイルとして保存する
  • CWebLogRoute: メッセージをウェブページの最後に表示する
  • CProfileLogRoute: プロファイルメッセージをウェブページ最後に表示する

情報: メッセージルーティングはリクエストサイクルの最後で onEndRequest イベントが発生したときに実行されます。 明示的に現在のリクエストの実行を終了するためには、die()exit()ではなく、CApplication::end() を呼び出してください。 なぜなら、CApplication::end() はメッセージが適切にログに記録されるように、onEndRequest イベントを発生させるからです。

3. メッセージのフィルタリング

先ほど述べたように、メッセージをログルートに送信する前に、レベルとカテゴリによってフィルタリングすることが可能です。 これはログルートの levels プロパティと、categories プロパティを設定することで実現されます。 複数のレベルやカテゴリを記述する場合には、カンマで区切ります。

メッセージカテゴリは xxx.yyy.zzz という書式なので、これらをカテゴリ階層として取り扱えます。 具体的に言うと、xxxxxx.yyy の親であり、さらに xxx.yyyxxx.yyy.zzz の親であるということです。 したがって、xxx.* と書くことで、xxx カテゴリとそれ以下の階層すべてを指し示すことができます。

4. コンテキスト情報をログに記録する

ログには、PHP の定義済み変数 (例えば、$_GET$_SERVER) や、セッション ID、ユーザ名など、付加的なコンテキスト情報も記録することが出来ます。 このことは、ログルートの CLogRoute::filter プロパティに適切なログフィルターを指定する事で実現できます。

フレームワークには便利な CLogFilter が内蔵されており、ほとんどの場合は、これを必要なログフィルターとして使うことが出来ます。 デフォルトでは、CLogFilter は、しばしば有用なシステムコンテキスト情報を含んでいる $_GET$_SERVER のような変数をログメッセージに追加します。 また CLogFilter は、各ログメッセージの前にセッション ID やユーザ名などを付加するように構成することも可能です。これらの付加的な情報は、膨大なログメッセージをチェックする際に、グローバル検索を格段に容易にしてくれるでしょう。

下記の構成は、コンテキスト情報のログへの記録を有効化する方法を示しています。 各ログルートがそれ自身のログフィルターを持つことが出来ることに注意して下さい。 そして、デフォルトでは、ログルートはログフィルターを持ちません。

array(
    ......
    'preload'=>array('log'),
    'components'=>array(
        ......
        'log'=>array(
            'class'=>'CLogRouter',
            'routes'=>array(
                array(
                    'class'=>'CFileLogRoute',
                    'levels'=>'error',
                    'filter'=>'CLogFilter',
                ),
                ... 他のログルート ...
            ),
        ),
    ),
)

Yii は、Yii::trace の呼出しによって記録されるメッセージにコールスタック情報を追加することをサポートしています。 この機能は、パフォーマンスを低下させるため、デフォルトでは無効に設定されています。 この機能を使用するためには、エントリスクリプトの先頭で (yii.php を include する前に) YII_TRACE_LEVEL という名前の定数を 0 より大きい整数として定義して下さい。 そうすると、Yii はすべてのトレースメッセージに、アプリケーションコードに属するコールスタックのファイル名と行番号を追加するようになります。 YII_TRACE_LEVEL の数値が、各コールスタックの記録されるべきレイヤ数を決定します。 この情報はトレースメッセージを発した場所を特定することを容易にしてくれますので、特に開発段階において有用なものです。

5. パフォーマンスプロファイリング

パフォーマンスプロファイリングは特殊なメッセージログです。 特定のコードブロックが必要とする実行時間を計測するために使われ、 パフォーマンスのボトルネックがどこにあるかを見つけ出します。

パフォーマンスプロファイリングを使うには、どのコードブロックが計測されるのかを指定する必要があります。 以下のメソッドを、ブロックの最初と最後に挿入することでマークをつけます。

Yii::beginProfile('blockID');
... 計測対象のコードブロック ...
Yii::endProfile('blockID');

blockID はコードブロックの一意な ID です

コードブロックは適切にネストする必要があることに注意してください。 すなわち、コードブロックを交差させることはできません。 二つのコードブロックは並列になっているか、一方が片方を完全に含んでいなければなりません。

プロファイルの結果を表示するには、CLogRouter アプリケーションコンポーネントをインストールする必要があります。 これは通常のメッセージルーティングのときと同じです。 CProfileLogRoute ルートが計測結果を現在のページの末尾に表示します。

array(
    ......
    'preload'=>array('log'),
    'components'=>array(
        ......
        'log'=>array(
            'class'=>'CLogRouter',
            'routes'=>array(
                array(
                    'class'=>'CProfileLogRoute',
                    'report'=>'summary',
                    // マークした全てのコードブロックの実行時間を表示
                    // report を callstack に設定することも可能
                ),
                ... 他のログルート ...
            ),
        ),
    ),
)

6. SQL 実行をプロファイルする

プロファイリングはデータベースを使用する場合に特に有用です。 というのは、SQL の実行がアプリケーションのパフォーマンスの主たるボトルネックとなることが多いからです。 各 SQL 実行の所要時間を計るためには、適切な場所に手作業で beginProfile 文と endProfile 文を挿入しても良いのですが、Yii はこの問題を解決するために、もっと体系的な手段を提供しています。

アプリケーション構成で CDbConnection::enableProfiling を true に設定すると、実行されるすべての SQL 文がプロファイルされます。 その結果は既に述べた CProfileLogRoute を使って簡単に表示することが出来、どの SQL 文にどれだけの時間が消費されているかを知ることができます。 また、CDbConnection::getStats() を呼んで、実行された SQL 文の総数と総所要時間を読み出すことも可能です。

$Id$

Be the first person to leave a comment

Please to leave your comment.