どうもこんにちは
Laravelから出力されるログを、
ファイルも作らずAmazon CouldWatch Logsに投げつける方法ないかなー、
と思っていたら、先人たちはやっているものですね、ありました。
こちらを参考に(ほぼパクリ)実装しました。
https://stackoverflow.com/questions/50814388/laravel-5-6-aws-cloudwatch-log
お決まりのようですが、今回も Laravel 5.8 でお送りします。
目次
事前準備
IAMユーザ
Amazon CloudWatch Logs を使うので、当然のようにIAMユーザを用意します。
CloudWatchFullAccess
権限を持っているIAMユーザを作成し、
アクセスキーとシークレットアクセスキーを取得しておきましょう。
もしかしたら、CloudWatchLogsFullAccess
でもよかったのかしら。。。
ライブラリインストール
composer経由でインストールします。
composer require maxbanton/cwh
これをインストールすると、依存関係で
aws/aws-sdk-php
monolog/monolog
もインストールされます(monolog/monologについては、laravelのフレームワークの時点で入ってますが)。
実装
ロガークラス
まずはこのクラスを作ります。
保存先はどこでもよいのだと思うのですが、今回はApp\Logger\CloudWatchLogger.phpです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<?php namespace App\Logging; use Aws\CloudWatchLogs\CloudWatchLogsClient; use Maxbanton\Cwh\Handler\CloudWatch; use Monolog\Logger; class CloudWatchLogger { /** * Create a custom Monolog instance. * * @param array $config * @return \Monolog\Logger */ public function __invoke(array $config) { $sdkParams = $config["sdk"]; $tags = $config["tags"] ?? [ ]; $name = $config["name"] ?? 'cloudwatch'; // Instantiate AWS SDK CloudWatch Logs Client $client = new CloudWatchLogsClient($sdkParams); // Log group name, will be created if none $groupName = config('app.name') . '-' . config('app.env'); // Log stream name, will be created if none try { $instance_id = file_get_contents("http://169.254.169.254/latest/meta-data/instance-id"); // <- ※1 } catch (\Exception $e) { $instance_id = null; } $streamName = $instance_id ?? config('app.hostname', 'localhost'); // <- ※2 // Days to keep logs, 14 by default. Set to `null` to allow indefinite retention. $retentionDays = $config["retention"]; // Instantiate handler (tags are optional) $handler = new CloudWatch($client, $groupName, $streamName, $retentionDays, 10000, $tags); // Create a log channel $logger = new Logger($name); // Set handler $logger->pushHandler($handler); return $logger; } } |
※1
今回は、ストリーム名にEC2のインスタンスIDを使いたかったので、こんな形にしています。
EC2からアクセスしない場合(ローカルの開発環境とか)は、ここでエラーになっちゃうので
try-catchで囲ってます。
※2
参考サイトには、configから返却するデフォルト値は設定してません。
何も考えずにやっていると、app.hostnameという設定値はどこにもないので
CloudWatch側でストリームが作られなくて小一時間困ってました。
なので、少なくともlocalhostが返ってくるようにしました。
config/logging.php
チャネルを作ります。config/logging.php の channels に、以下を追加します。
ロガークラスの参照先は、適宜。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
'cloudwatch' => [ 'driver' => 'custom', 'via' => \App\Logging\CloudWatchLogger::class, // <- 上で作成したロガークラス 'sdk' => [ 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'version' => 'latest', 'credentials' => [ 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY') ] ], 'retention' => env('CLOUDWATCH_LOG_RETENTION', 7), // <- ログ保存期間(null の場合は無制限) ], |
.env
.envファイルを書き換えます。
環境ごとに作成している場合は、適宜読み替えてください。
1 2 3 4 5 |
LOG_CHANNEL=cloudwatch # デフォルトをCloudWatch Logsにする場合は、こちらを設定 AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXXX # IAMユーザのアクセスキー AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXX # IAMユーザのシークレットアクセスキー AWS_DEFAULT_REGION=ap-northeast-1 # CloudWatch Logsのリージョン ap-northeast-1 は東京 |
実行してみる
下のような感じでログを出力すると、
あら不思議、CloudWatch Logsに出力されています。
※AWSコンソールのスクリーンショットはご勘弁を。
1 2 3 4 5 6 7 8 9 10 |
use Illuminate\Support\Facades\Log; public function outputLogs() { Log::debug('debug message!'); Log::info('info message!'); Log::warning('warning message!'); Log::error('error message!'); Log::emergency('emergency message!'); } |
まとめ
あとは、ログをS3に定期的に移してあげたり、なんだったり。
EC2に余計なファイルを増やしたくない一心だったので、
目的は達成です。
r.tanakaがお届けしました。