0 フォロワー

ロギング

Yii は、高度にカスタマイズ可能で拡張性の高い強力なロギングフレームワークを提供します。このフレームワークを使用すると、さまざまなタイプのメッセージを簡単にログに記録し、フィルタリングし、ファイル、データベース、メールなど、さまざまなターゲットに収集できます。

Yii ロギングフレームワークの使用には、次の手順が含まれます。

  • コードのさまざまな場所でログメッセージを記録します。
  • アプリケーション設定でログターゲットを設定して、ログメッセージをフィルタリングおよびエクスポートします。
  • さまざまなターゲット(例:Yii デバッガー)によってエクスポートされた、フィルタリングされたログメッセージを確認します。

このセクションでは、主に最初の 2 つの手順について説明します。

ログメッセージ

ログメッセージの記録は、次のロギングメソッドのいずれかを呼び出すほど簡単です。

  • Yii::debug():コードの実行方法をトレースするためのメッセージを記録します。これは主に開発用途です。
  • Yii::info():役立つ情報を伝えるメッセージを記録します。
  • Yii::warning():予期しないことが発生したことを示す警告メッセージを記録します。
  • Yii::error():できるだけ早く調査する必要がある致命的なエラーを記録します。

これらのロギングメソッドは、さまざまな重大度レベルカテゴリでログメッセージを記録します。それらは同じ関数シグネチャ `function ($message, $category = 'application')` を共有します。ここで、`$message` は記録されるログメッセージを表し、`$category` はログメッセージのカテゴリです。次の例のコードは、デフォルトのカテゴリ `application` でトレースメッセージを記録します。

Yii::debug('start calculating average revenue');

情報:ログメッセージは文字列だけでなく、配列やオブジェクトなどの複雑なデータでもかまいません。ログターゲットは、ログメッセージを適切に処理する責任があります。デフォルトでは、ログメッセージが文字列でない場合、yii\helpers\VarDumper::export() を呼び出すことによって文字列としてエクスポートされます。

ログメッセージの整理とフィルタリングを効果的に行うために、各ログメッセージに適切なカテゴリを指定することをお勧めします。カテゴリには階層的な命名スキームを使用できます。これにより、ログターゲットがカテゴリに基づいてメッセージをフィルタリングしやすくなります。シンプルながらも効果的な命名スキームとしては、カテゴリ名にPHPのマジック定数__METHOD__を使用する方法があります。これはYiiフレームワークのコアコードでも使用されているアプローチです。例:

Yii::debug('start calculating average revenue', __METHOD__);

__METHOD__定数は、定数が出現するメソッド名(完全修飾クラス名で接頭辞が付けられる)として評価されます。たとえば、上記のコード行がメソッド内で呼び出された場合、文字列'app\controllers\RevenueController::calculate'と等しくなります。

情報:上記で説明されているログメソッドは、実際にはlog()メソッドへのショートカットです。loggerオブジェクトはシングルトンであり、Yii::getLogger()という式でアクセスできます。十分な数のメッセージがログに記録された場合、またはアプリケーションが終了した場合、loggerオブジェクトはメッセージディスパッチャを呼び出して、登録済みのログターゲットに記録されたログメッセージを送信します。

ログターゲット

ログターゲットは、yii\log\Targetクラスまたはその子クラスのインスタンスです。ログメッセージを重大度レベルとカテゴリでフィルタリングし、特定の媒体に出力します。たとえば、データベースターゲットはフィルタリングされたログメッセージをデータベーステーブルに出力し、メールターゲットはログメッセージを指定されたメールアドレスに出力します。

アプリケーション構成のlogアプリケーションコンポーネントを介して、複数のログターゲットをアプリケーションに登録できます。以下に例を示します。

return [
    // the "log" component must be loaded during bootstrapping time
    'bootstrap' => ['log'],
    // the "log" component process messages with timestamp. Set PHP timezone to create correct timestamp
    'timeZone' => 'America/Los_Angeles',
    'components' => [
        'log' => [
            'targets' => [
                [
                    'class' => 'yii\log\DbTarget',
                    'levels' => ['error', 'warning'],
                ],
                [
                    'class' => 'yii\log\EmailTarget',
                    'levels' => ['error'],
                    'categories' => ['yii\db\*'],
                    'message' => [
                       'from' => ['log@example.com'],
                       'to' => ['admin@example.com', 'developer@example.com'],
                       'subject' => 'Database errors at example.com',
                    ],
                ],
            ],
        ],
    ],
];

注意:logコンポーネントは、ブートストラップ時にロードする必要があります。これにより、ターゲットにログメッセージを迅速にディスパッチできます。そのため、上記のようにbootstrap配列にリストされています。

上記のコードでは、yii\log\Dispatcher::$targetsプロパティに2つのログターゲットが登録されています。

  • 最初のターゲットはエラーと警告メッセージを選択し、データベーステーブルに保存します。
  • 2番目のターゲットは、名前がyii\db\で始まるカテゴリのエラーメッセージを選択し、admin@example.comdeveloper@example.comの両方にメールで送信します。

Yiiには、以下の組み込みログターゲットがあります。これらのクラスに関するAPIドキュメントを参照して、設定方法と使用方法を確認してください。

  • yii\log\DbTarget:ログメッセージをデータベーステーブルに保存します。
  • yii\log\EmailTarget:ログメッセージを事前に指定されたメールアドレスに送信します。
  • yii\log\FileTarget:ログメッセージをファイルに保存します。
  • yii\log\SyslogTarget:PHP関数syslog()を呼び出して、ログメッセージをsyslogに保存します。

次に、すべてのログターゲットに共通する機能について説明します。

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

各ログターゲットについて、そのlevelsプロパティとcategoriesプロパティを設定して、ターゲットが処理するメッセージの重大度レベルとカテゴリを指定できます。

levelsプロパティには、以下の値の1つまたは複数を含む配列が設定されます。

  • errorYii::error()によってログに記録されたメッセージに対応します。
  • warningYii::warning()によってログに記録されたメッセージに対応します。
  • infoYii::info()によってログに記録されたメッセージに対応します。
  • traceYii::debug()によってログに記録されたメッセージに対応します。
  • profileYii::beginProfile()Yii::endProfile()によってログに記録されたメッセージに対応します。これはプロファイリングのサブセクションで詳しく説明します。

levelsプロパティを指定しない場合、ターゲットは任意の重大度レベルのメッセージを処理します。

categoriesプロパティには、メッセージカテゴリ名またはパターンの配列が設定されます。ターゲットは、そのカテゴリがこの配列のいずれかのパターンに一致するメッセージのみを処理します。カテゴリパターンは、最後にアスタリスク*が付いたカテゴリ名です。カテゴリ名は、パターンのプレフィックスと同じで始まる場合、カテゴリパターンと一致します。たとえば、yii\db\Commandクラスで記録されたログメッセージのカテゴリ名として、yii\db\Command::executeyii\db\Command::queryが使用されます。これらは両方ともパターンyii\db\*と一致します。

categoriesプロパティを指定しない場合、ターゲットは任意のカテゴリのメッセージを処理します。

categoriesプロパティを使用して許可されるカテゴリを指定することに加えて、exceptプロパティを使用して特定のカテゴリを除外することもできます。メッセージのカテゴリがこのプロパティのいずれかのパターンと一致する場合、ターゲットによって処理されません。

次のターゲット構成は、ターゲットがyii\db\*またはyii\web\HttpException:*と一致するカテゴリのエラーと警告メッセージのみを処理し、yii\web\HttpException:404は処理しないことを指定しています。

[
    'class' => 'yii\log\FileTarget',
    'levels' => ['error', 'warning'],
    'categories' => [
        'yii\db\*',
        'yii\web\HttpException:*',
    ],
    'except' => [
        'yii\web\HttpException:404',
    ],
]

情報:エラーハンドラによってHTTP例外がキャッチされると、yii\web\HttpException:ErrorCode形式のカテゴリ名でエラーメッセージがログに記録されます。たとえば、yii\web\NotFoundHttpExceptionは、カテゴリyii\web\HttpException:404のエラーメッセージを発生させます。

メッセージのフォーマット

ログターゲットは、フィルタリングされたログメッセージを特定のフォーマットで出力します。たとえば、yii\log\FileTargetクラスのログターゲットをインストールすると、runtime/log/app.logファイルに次のようなログメッセージが表示される場合があります。

2014-10-04 18:10:15 [::1][][-][trace][yii\base\Module::getModule] Loading module: debug

デフォルトでは、ログメッセージはyii\log\Target::formatMessage()によって以下のようにフォーマットされます。

Timestamp [IP address][User ID][Session ID][Severity Level][Category] Message Text

カスタマイズされたメッセージプレフィックスを返すPHPのcallableを設定することで、yii\log\Target::$prefixプロパティを構成してこのフォーマットをカスタマイズできます。たとえば、次のコードは、各ログメッセージに現在のユーザーIDをプレフィックスとして付けるようにログターゲットを設定します(プライバシーの理由から、IPアドレスとセッションIDは削除されています)。

[
    'class' => 'yii\log\FileTarget',
    'prefix' => function ($message) {
        $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;
        $userID = $user ? $user->getId(false) : '-';
        return "[$userID]";
    }
]

メッセージプレフィックスに加えて、ログターゲットは各ログメッセージのバッチにいくつかのコンテキスト情報を追加します。デフォルトでは、これらのグローバルPHP変数の値が含まれます:$_GET$_POST$_FILES$_COOKIE$_SESSION$_SERVERyii\log\Target::$logVarsプロパティを構成して、ログターゲットに含めるグローバル変数の名前を指定することで、この動作を調整できます。たとえば、次のログターゲット構成では、$_SERVER変数の値のみがログメッセージに追加されます。

[
    'class' => 'yii\log\FileTarget',
    'logVars' => ['_SERVER'],
]

コンテキスト情報の包含を完全に無効にするには、logVarsを空の配列に設定します。または、コンテキスト情報の提供方法を独自に実装する場合は、yii\log\Target::getContextMessage()メソッドをオーバーライドできます。

リクエストフィールドの一部にログに記録したくない機密情報(パスワード、アクセストークンなど)が含まれている場合、maskVarsプロパティを追加で構成できます。デフォルトでは、以下のリクエストパラメータは***でマスクされます:$_SERVER[HTTP_AUTHORIZATION]$_SERVER[PHP_AUTH_USER]$_SERVER[PHP_AUTH_PW]ですが、独自に設定できます。

[
    'class' => 'yii\log\FileTarget',
    'logVars' => ['_SERVER'],
    'maskVars' => ['_SERVER.HTTP_X_PASSWORD']
]

メッセージトレースレベル

開発中は、各ログメッセージの出所を確認することがよくあります。これは、次のようにlogコンポーネントのtraceLevelプロパティを構成することで実現できます。

return [
    'bootstrap' => ['log'],
    'components' => [
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [...],
        ],
    ],
];

上記のアプリケーション構成では、YII_DEBUGがオンの場合はtraceLevelを3に、YII_DEBUGがオフの場合は0に設定しています。つまり、YII_DEBUGがオンの場合、各ログメッセージには、ログメッセージが記録されたコールスタックの最大3レベルが追加され、YII_DEBUGがオフの場合は、コールスタック情報は含まれません。

情報:コールスタック情報の取得は容易ではありません。そのため、この機能は開発中またはアプリケーションのデバッグ時のみ使用するようにしてください。

メッセージのフラッシュとエクスポート

前述のように、ログメッセージはloggerオブジェクトによって配列に保持されます。この配列によるメモリ消費を制限するために、loggerは、配列に特定数のログメッセージが蓄積されるたびに、記録されたメッセージをログターゲットにフラッシュします。この数は、logコンポーネントのflushIntervalプロパティを構成することでカスタマイズできます。

return [
    'bootstrap' => ['log'],
    'components' => [
        'log' => [
            'flushInterval' => 100,   // default is 1000
            'targets' => [...],
        ],
    ],
];

情報:アプリケーションが終了したときにもメッセージのフラッシュが行われます。これにより、ログターゲットは完全なログメッセージを受信できます。

loggerオブジェクトがログメッセージをログターゲットにフラッシュしても、すぐにエクスポートされるわけではありません。代わりに、メッセージのエクスポートは、ログターゲットがフィルタリングされたメッセージの特定の数を蓄積した場合にのみ行われます。この数は、個々のログターゲットexportIntervalプロパティを構成することでカスタマイズできます。以下に例を示します。

[
    'class' => 'yii\log\FileTarget',
    'exportInterval' => 100,  // default is 1000
]

フラッシュとエクスポートレベルの設定のため、デフォルトでは、Yii::debug()またはその他のログメソッドを呼び出しても、ログターゲットにログメッセージがすぐに表示されるわけではありません。これは、長時間実行されるコンソールアプリケーションでは問題になる可能性があります。各ログメッセージをログターゲットにすぐに表示するには、以下のようにflushIntervalexportIntervalの両方を1に設定する必要があります。

return [
    'bootstrap' => ['log'],
    'components' => [
        'log' => [
            'flushInterval' => 1,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'exportInterval' => 1,
                ],
            ],
        ],
    ],
];

注意:頻繁なメッセージのフラッシュとエクスポートは、アプリケーションのパフォーマンスを低下させます。

ログターゲットの切り替え

ログターゲットのenabledプロパティを構成することで、ログターゲットを有効または無効にできます。ログターゲット構成を介して、またはコード内の次のPHPステートメントを使用して実行できます。

Yii::$app->log->targets['file']->enabled = false;

上記のコードでは、targets配列で文字列キーを使用することによって、以下のようにターゲットにfileという名前を付ける必要があります。

return [
    'bootstrap' => ['log'],
    'components' => [
        'log' => [
            'targets' => [
                'file' => [
                    'class' => 'yii\log\FileTarget',
                ],
                'db' => [
                    'class' => 'yii\log\DbTarget',
                ],
            ],
        ],
    ],
];

バージョン2.0.13以降、enabledをcallableで構成して、ログターゲットを有効にするかどうかの動的な条件を定義できます。yii\log\Target::setEnabled()のドキュメントを参照して例を確認してください。

新しいターゲットの作成

新しいログターゲットクラスの作成は非常に簡単です。yii\log\Target::$messages配列の内容を指定された媒体に送信するyii\log\Target::export()メソッドを実装する必要があります。yii\log\Target::formatMessage()メソッドを呼び出して、各メッセージをフォーマットできます。詳細については、Yiiリリースに含まれるログターゲットクラスのいずれかを参照してください。

ヒント:独自のloggerを作成する代わりに、PSRログターゲット拡張を使用して、MonologなどのPSR-3互換のloggerを試すことができます。

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

パフォーマンスプロファイリングは、特定のコードブロックの実行時間を測定し、パフォーマンスボトルネックを特定するために使用される特殊なメッセージロギングです。例えば、yii\db\Commandクラスは、各DBクエリの実行時間を調べるためにパフォーマンスプロファイリングを使用しています。

パフォーマンスプロファイリングを使用するには、最初にプロファイリングする必要があるコードブロックを特定します。次に、各コードブロックを以下のように囲みます。

\Yii::beginProfile('myBenchmark');

...code block being profiled...

\Yii::endProfile('myBenchmark');

ここで、myBenchmarkはコードブロックを一意に識別するトークンを表します。後でプロファイリング結果を調べるときに、このトークンを使用して、対応するコードブロックで費やされた時間を特定します。

beginProfileendProfileのペアが適切にネストされていることを確認することが重要です。例えば、

\Yii::beginProfile('block1');

    // some code to be profiled

    \Yii::beginProfile('block2');
        // some other code to be profiled
    \Yii::endProfile('block2');

\Yii::endProfile('block1');

\Yii::endProfile('block1')が欠落していたり、\Yii::endProfile('block1')\Yii::endProfile('block2')の順序が入れ替わっていたりすると、パフォーマンスプロファイリングは機能しません。

プロファイリングされる各コードブロックに対して、重大度レベルprofileのログメッセージが記録されます。そのようなメッセージを収集してエクスポートするために、ログターゲット を設定できます。Yiiデバッガーには、プロファイリング結果を表示する組み込みのパフォーマンスプロファイリングパネルがあります。

タイプミスを発見した、またはこのページの改善が必要だと考えますか?
Githubで編集する !