Amazon Web Services ブログ

Amazon CloudWatch LogsとMonologを使用したPHPアプリケーションログ

ロギングとデバッグ情報は、さまざまな角度からアプローチできます。アプリケーションフレームワークを使用する場合でも、一からコーディングする場合でも、異なるプロジェクト間で使い慣れたコンポーネントやツールを使用することは常に快適です。今回の例では、PHPアプリケーションからのログを、Amazon CloudWatch Logsに出力させます。これを実現するために、すでに普及しよく利用されている標準に準拠した既存のソリューションを使いたいと思っていました。これらの理由からオープンソースのログライブラリであるPHP Monolog(https://github.com/Seldaek/monolog)を使用します。

PHP Monolog

新しいPHPアプリケーション、フレームワーク、またはサービスを扱う人にとって、ソリューション間で頻繁に現れる技術の選択肢の1つとしてアプリケーションログ用のMonologを利用することが挙げられます。PHP Monologは標準に準拠したPHPライブラリで、開発者はデータベース、ファイル、ソケット、さまざまなサービスなどのさまざまな種類のフォーマットにログを送信できます。PHP MonologはPSR-3で定義されたPHPロギングの標準化よりも前に、PSR-3のインターフェースと標準を実装していました。これにより、Monologはロギングライブラリ用の共通インタフェースに準拠しています。CloudWatch LogsでMonologを使用すると、PSR-3互換ロギングソリューションが作成されます。Monologは、Laravel、Symfony、CakePHPなど多くの異なるアプリケーションやフレームワークで使用できます。今日の例は、アプリケーションログの目的で、PHP Monologを使用してCloudWatchLogsに情報を送信し、CloudWatchのアラームと通知でアプリケーションデータを使用できる構造とプロセスを構築することです。これにより、アプリケーションからのログをキーにAmazon EC2 AutoScalingのようなサービスのアクションに使用することができます。

Amazon CloudWatch Logs

顧客第一主義の組織として、AWSはお客様やAWSパートナーが要望する重要な機能やサービスを絶えず構築し、リリースしています。本日、ハイライトされるサービスの1つがAmazon CloudWatch Logsです。CloudWatch Logsを使用すると、アプリケーション、オペレーティングシステムおよびインスタンス、AWSサービス、およびその他のさまざまなソースからのログファイルの情報を保存できます。以前のブログ記事では、CloudWatch Logsをさまざまなプログラミング例とともに紹介しました。

ブログ記事にはCloudWatch Logsを使用してアプリケーションからのエントリを保存するPHPの例があります。この例を使用してスタンドアロンソリューションとして拡張し、アプリケーション内からCloudWatch Logsにログを送ることができます。この例では、PHP Monologを活用してこの機能を強化します。

Monologの実装

Monologを使用開始するには、Composer(https://getcomposer.org/)を使用して必要なライブラリをインストールします。以下の手順では、AWS SDK for PHP、PHP Monologおよび CloudWatch Logsへのログを有効にするMonologのアドオンをインストールします。

curl -sS https://getcomposer.org/installer | php 
php composer.phar require aws/aws-sdk-php 
php composer.phar require monolog/monolog 
php composer.phar require maxbanton/cwh:^1.0

あるいは、次のエントリをcomposer.jsonファイルにコピーし、php composer.phar installコマンドでインストールすることもできます。

{
    "minimum-stability": "stable",
    "require": {
        "aws/aws-sdk-php": "^3.24",
        "aws/aws-php-sns-message-validator": "^1.1",
        "monolog/monolog": "^1.21",
        "maxbanton/cwh": "^1.0"
    }
}
Local logging

PHP Monologが利用できるようになったので、それを実装しテストすることができます。まず、1ファイルにロギングする例を示します。

require "vendor/autoload.php";

use Monolog\Logger;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;

$logFile = "testapp_local.log";

$logger = new Logger('TestApp01');
$formatter = new LineFormatter(null, null, false, true);
$infoHandler = new StreamHandler(__DIR__."/".$logFile, Logger::INFO);
$infoHandler->setFormatter($formatter);
$logger->pushHandler($infoHandler);
$logger->info('Initial test of application logging.');

前の例では、先ほどインストールしたコンポーザーライブラリが必要です。新しいロガーラインは「TestApp01」として、チャンネル名を設定します。次の行は、未使用のログ項目の括弧を削除する新しいLineFormatterを作成します。次の行は、識別されるファイル名として先を確立しtestapp_local.log、およびINFOログレベルでそれを関連付けます。次に、ストリームハンドラにフォーマットを適用します。次に、更新された形式のストリームハンドラをハンドラリストに追加します。最後に、INFOというログレベルで新しいメッセージが記録されます。ログレベルとハンドラの詳細については、Monolog GitHubページIETF RFC 5424およびPSR-3を参照してください。

機能を確認するために、ログファイルの内容を表示できるようになりました:

Syslog logging

シンプルなログエントリをローカルファイルに書き込むことができたので、次の例ではシステムのSyslogを使用してイベントを記録しています。

$logger = new Logger($appName);

$localFormatter = new LineFormatter(null, null, false, true);
$syslogFormatter = new LineFormatter("%channel%: %level_name%: %message% %context% %extra%",null,false,true);

$infoHandler = new StreamHandler(__DIR__."/".$logFile, Logger::INFO);
$infoHandler->setFormatter($localFormatter);

$warnHandler = new SyslogHandler($appName, $facility, Logger::WARNING);
$warnHandler->setFormatter($syslogFormatter);

$logger->pushHandler($warnHandler);
$logger->pushHandler($infoHandler);

$logger->info('Test of PHP application logging.');
$logger->warn('Test of the warning system logging.');

syslogメッセージのフォーマットが$syslogFormatterという値で変更されていることがわかります。syslogは各ログエントリに日付/時刻を挿入するので、これらの値をログテキストに含める必要はありません。syslog機能はlocal0に設定され、すべてのWARNINGメッセージはsyslogにともに送信され、INFOレベルのメッセージとWARNINGレベルのメッセージはローカルファイルに記録されます。Syslog機能とログレベルに関する追加情報は、Syslog Wikipediaのページを参照してください。

Logging to CloudWatch Logs

Monologの基本的な使い方を見てきたので、いくつかのログをCloudWatch Logsに送りましょう。Monologライブラリ用のAmazon Web Services CloudWatch Logs Handlerを使用して、MonologをCloudWatch Logsに統合することができます。この例では、認証アプリケーションによってログ情報が生成されます。

use Aws\CloudWatchLogs\CloudWatchLogsClient;
use Maxbanton\Cwh\Handler\CloudWatch;
use Monolog\Logger;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogHandler;

$logFile = "testapp_local.log";
$appName = "TestApp01";
$facility = "local0";

// Get instance ID:
$url = "http://169.254.169.254/latest/meta-data/instance-id";
$instanceId = file_get_contents($url);

$cwClient = new CloudWatchLogsClient($awsCredentials);
// Log group name, will be created if none
$cwGroupName = 'php-app-logs';
// Log stream name, will be created if none
$cwStreamNameInstance = $instanceId;
// Instance ID as log stream name
$cwStreamNameApp = "TestAuthenticationApp";
// Days to keep logs, 14 by default
$cwRetentionDays = 90;

$cwHandlerInstanceNotice = new CloudWatch($cwClient, $cwGroupName, $cwStreamNameInstance, $cwRetentionDays, 10000, [ 'application' => 'php-testapp01' ],Logger::NOTICE);
$cwHandlerInstanceError = new CloudWatch($cwClient, $cwGroupName, $cwStreamNameInstance, $cwRetentionDays, 10000, [ 'application' => 'php-testapp01' ],Logger::ERROR);
$cwHandlerAppNotice = new CloudWatch($cwClient, $cwGroupName, $cwStreamNameApp, $cwRetentionDays, 10000, [ 'application' => 'php-testapp01' ],Logger::NOTICE);

$logger = new Logger('PHP Logging');

$formatter = new LineFormatter(null, null, false, true);
$syslogFormatter = new LineFormatter("%channel%: %level_name%: %message% %context% %extra%",null,false,true);
$infoHandler = new StreamHandler(__DIR__."/".$logFile, Logger::INFO);
$infoHandler->setFormatter($formatter);

$warnHandler = new SyslogHandler($appName, $facility, Logger::WARNING);
$warnHandler->setFormatter($syslogFormatter);

$cwHandlerInstanceNotice->setFormatter($formatter);
$cwHandlerInstanceError->setFormatter($formatter);
$cwHandlerAppNotice->setFormatter($formatter);

$logger->pushHandler($warnHandler);
$logger->pushHandler($infoHandler);
$logger->pushHandler($cwHandlerInstanceNotice);
$logger->pushHandler($cwHandlerInstanceError);
$logger->pushHandler($cwHandlerAppNotice);

$logger->info('Initial test of application logging.');
$logger->warn('Test of the warning system logging.');
$logger->notice('Application Auth Event: ',[ 'function'=>'login-action','result'=>'login-success' ]);
$logger->notice('Application Auth Event: ',[ 'function'=>'login-action','result'=>'login-failure' ]);
$logger->error('Application ERROR: System Error');

 

この例では、アプリケーション認証イベントはPHP配列として渡され、JSONとしてCloudWatch Logsに表示されます。login-successおよびlogin-failureの結果を伴うイベントは、インスタンスIDに関連付けられたログストリームと、アプリケーション名に関連付けられたログストリームの両方に送信されます。

 

これらの異なるストリームの場所を使用して、インスタンスごとまたはアプリケーションごとにメトリックとアラームを作成できます。過去5分間にアプリケーションにログインしたユーザーの総数に対するメトリックを作成するとします。イベント・グループを選択し、メトリック・フィルターの作成を選択します。

次のページでは、フィルタとテストを同じウィンドウで作成できます。フィルタデータについては、ログエントリのJSON文字列を使用します。すべての正常なログインを抽出するには、次の文字列を入力します。

{ $.result = login-success }

下記のように、フィルタの詳細が表示されます。フィルタ名を識別しやすい値に変更しました。メトリック名前空間にはアプリケーション名に関連付けられた値があり、メトリック名にはログイン成功数が反映されます。

CloudWatch Logsを介して受け取ったこの情報に基づいて、アラームを作成して通知を送信したり、(Amazon EC2スケーリングの決定などの)アクションとることができます。

これらの値を使用すると、5分以内に50回以上ログインが成功するたびに警告が表示されます。

Laravel logging

Monologは、一般的なLaravel PHPフレームワークを含む多くのPHPアプリケーションとフレームワークのロギングソリューションとして使用されています。この例では、Laravel内でMonolog with CloudWatch Logsの使用を示します。最初のステップは、Laravelアプリケーションの現在のログ設定を調べることです。アプリケーションルート内でconfig/app.phpを開くと、さまざまなログ設定が表示されます。デフォルトでは、Laravelはデバッグのベースラインログレベルを使用して単一のログファイルにログするように設定されています。

次に、ここからの説明と例を使用して、Laravel内のサービスプロバイダとしてAWS SDK for PHPを追加します。

また、CloudWatchログのMonologライブラリをcomposer.jsonファイルに追加して、アプリケーションに含めることもできます(図を参照)。

これで、現在のLaravel Monolog構成をカスタム構成で拡張する必要があります。この手順の詳細は、Laravel Error and Loggingページを参照してください。以下は、bootstrap/app.phpファイルへの追加例です。

use Maxbanton\Cwh\Handler\CloudWatch;

$app->configureMonologUsing( function($monolog) {

    $cwClient = App::make('aws')->createClient('CloudWatchLogs');
    $cwGroupName = env('AWS_CWL_GROUP', 'laravel-app-logs');
    $cwStreamNameApp = env('AWS_CWL_APP', 'laravel-app-name');
    $cwTagName = env('AWS_CWL_TAG_NAME', 'application');
    $cwTagValue = env('AWS_CWL_TAG_VALUE', 'laravel-testapp01');
    $cwRetentionDays = 90;
    $cwHandlerApp = new CloudWatch($cwClient, $cwGroupName, $cwStreamNameApp, $cwRetentionDays, 10000, [ $cwTagName => $cwTagValue ] );

    $monolog->pushHandler($cwHandlerApp);
});

テスト目的のために、routes/web.phpのテストルートにロギングコールを追加します。

Route::get('/test', function () {
    Log::warning('Clicking on test link!!!');
    return view('test');
});

テストルートが呼び出されると、ログはCloudWatch Logsに表示されるようになりました。

結論

この例では、PHP Monologを使用してローカルファイル、syslogおよびCloudWatch Logsにログを書き込む方法を紹介しました。また、一般的なPHPアプリケーションフレームワーク内で、MonologとCloudWatch Logsの統合を実施しました。最後に、CloudWatch Logsメトリックフィルタを作成し、CloudWatch Alarmsに適用して、ログからのデータを通知とともにスケーラビリティの決定とともに実行可能にする方法をご紹介しました。CloudWatch Logsは、PHPアプリケーションの集中的にロギング機能を提供し、Monologと組み合わせることで、確立されたプロジェクトやカスタムエンゲージメント内で使用するライブラリの可用性を保証します。
(翻訳はSA 森が担当しました。原文はこちら)