From d142cf56cd50f3820228491facc617d0d0b5bd9d Mon Sep 17 00:00:00 2001 From: Naren Date: Mon, 20 Jul 2020 20:34:32 +0400 Subject: [PATCH 1/7] try as custom driver --- config/logging.php | 3 + src/Logger.php | 115 ++++++++++++++++++++ src/Providers/CloudWatchServiceProvider.php | 95 +--------------- 3 files changed, 120 insertions(+), 93 deletions(-) create mode 100644 src/Logger.php diff --git a/config/logging.php b/config/logging.php index 3fccbc7..d1e6a91 100644 --- a/config/logging.php +++ b/config/logging.php @@ -1,7 +1,9 @@ [ + 'driver' => 'custom', 'name' => env('CLOUDWATCH_LOG_NAME', ''), 'region' => env('CLOUDWATCH_LOG_REGION', ''), 'credentials' => [ @@ -21,5 +23,6 @@ true ); }, + 'via' => Logger::class ], ]; diff --git a/src/Logger.php b/src/Logger.php new file mode 100644 index 0000000..f26b9e0 --- /dev/null +++ b/src/Logger.php @@ -0,0 +1,115 @@ +app = $app; + } + + public function __invoke(array $config) + { + $loggingConfig = $config; + $cwClient = new CloudWatchLogsClient($this->getCredentials()); + + $streamName = $loggingConfig['stream_name']; + $retentionDays = $loggingConfig['retention']; + $groupName = $loggingConfig['group_name']; + $batchSize = isset($loggingConfig['batch_size']) ? $loggingConfig['batch_size'] : 10000; + + $logHandler = new CloudWatch($cwClient, $groupName, $streamName, $retentionDays, $batchSize); + $logger = new \Monolog\Logger($loggingConfig['name']); + + $formatter = $this->resolveFormatter($loggingConfig); + $logHandler->setFormatter($formatter); + $logger->pushHandler($logHandler); + + return $logger; + } + + /** + * This is the way config should be defined in config/logging.php + * in key cloudwatch. + * + * 'cloudwatch' => [ + * 'name' => env('CLOUDWATCH_LOG_NAME', ''), + * 'region' => env('CLOUDWATCH_LOG_REGION', ''), + * 'credentials' => [ + * 'key' => env('CLOUDWATCH_LOG_KEY', ''), + * 'secret' => env('CLOUDWATCH_LOG_SECRET', '') + * ], + * 'stream_name' => env('CLOUDWATCH_LOG_STREAM_NAME', 'laravel_app'), + * 'retention' => env('CLOUDWATCH_LOG_RETENTION_DAYS', 14), + * 'group_name' => env('CLOUDWATCH_LOG_GROUP_NAME', 'laravel_app'), + * 'version' => env('CLOUDWATCH_LOG_VERSION', 'latest'), + * ] + * + * @return array + * + * @throws \Pagevamp\Exceptions\IncompleteCloudWatchConfig + */ + protected function getCredentials() + { + $loggingConfig = $this->app->make('config')->get('logging.channels'); + + if (!isset($loggingConfig['cloudwatch'])) { + throw new IncompleteCloudWatchConfig('Configuration Missing for Cloudwatch Log'); + } + + $cloudWatchConfigs = $loggingConfig['cloudwatch']; + + if (!isset($cloudWatchConfigs['region'])) { + throw new IncompleteCloudWatchConfig('Missing region key-value'); + } + + $awsCredentials = [ + 'region' => $cloudWatchConfigs['region'], + 'version' => $cloudWatchConfigs['version'], + ]; + + if ($cloudWatchConfigs['credentials']['key']) { + $awsCredentials['credentials'] = $cloudWatchConfigs['credentials']; + } + + return $awsCredentials; + } + + /** + * @return mixed|LineFormatter + * + * @throws IncompleteCloudWatchConfig + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + private function resolveFormatter(array $configs) + { + if (!isset($configs['formatter'])) { + return new LineFormatter( + '%channel%: %level_name%: %message% %context% %extra%', + null, + false, + true + ); + } + + $formatter = $configs['formatter']; + + if (\is_string($formatter) && class_exists($formatter)) { + return $this->app->make($formatter); + } + + if (\is_callable($formatter)) { + return $formatter($configs); + } + + throw new IncompleteCloudWatchConfig('Formatter is missing for the logs'); + } +} diff --git a/src/Providers/CloudWatchServiceProvider.php b/src/Providers/CloudWatchServiceProvider.php index 64b4e18..12e3a68 100644 --- a/src/Providers/CloudWatchServiceProvider.php +++ b/src/Providers/CloudWatchServiceProvider.php @@ -2,9 +2,7 @@ namespace Pagevamp\Providers; -use Aws\CloudWatchLogs\CloudWatchLogsClient; use Illuminate\Support\ServiceProvider; -use Maxbanton\Cwh\Handler\CloudWatch; use Monolog\Formatter\LineFormatter; use Monolog\Logger; use Pagevamp\Exceptions\IncompleteCloudWatchConfig; @@ -43,22 +41,9 @@ public function boot() public function getLogger() { - $cwClient = new CloudWatchLogsClient($this->getCredentials()); + $logger = new \Pagevamp\Logger($this->app); $loggingConfig = $this->app->make('config')->get('logging.channels.cloudwatch'); - - $streamName = $loggingConfig['stream_name']; - $retentionDays = $loggingConfig['retention']; - $groupName = $loggingConfig['group_name']; - $batchSize = isset($loggingConfig['batch_size']) ? $loggingConfig['batch_size'] : 10000; - - $logHandler = new CloudWatch($cwClient, $groupName, $streamName, $retentionDays, $batchSize); - $logger = new Logger($loggingConfig['name']); - - $formatter = $this->resolveFormatter($loggingConfig); - $logHandler->setFormatter($formatter); - $logger->pushHandler($logHandler); - - return $logger; + return $logger($loggingConfig); } /** @@ -80,80 +65,4 @@ public function register() } } - /** - * This is the way config should be defined in config/logging.php - * in key cloudwatch. - * - * 'cloudwatch' => [ - * 'name' => env('CLOUDWATCH_LOG_NAME', ''), - * 'region' => env('CLOUDWATCH_LOG_REGION', ''), - * 'credentials' => [ - * 'key' => env('CLOUDWATCH_LOG_KEY', ''), - * 'secret' => env('CLOUDWATCH_LOG_SECRET', '') - * ], - * 'stream_name' => env('CLOUDWATCH_LOG_STREAM_NAME', 'laravel_app'), - * 'retention' => env('CLOUDWATCH_LOG_RETENTION_DAYS', 14), - * 'group_name' => env('CLOUDWATCH_LOG_GROUP_NAME', 'laravel_app'), - * 'version' => env('CLOUDWATCH_LOG_VERSION', 'latest'), - * ] - * - * @return array - * - * @throws \Pagevamp\Exceptions\IncompleteCloudWatchConfig - */ - protected function getCredentials() - { - $loggingConfig = $this->app->make('config')->get('logging.channels'); - - if (!isset($loggingConfig['cloudwatch'])) { - throw new IncompleteCloudWatchConfig('Configuration Missing for Cloudwatch Log'); - } - - $cloudWatchConfigs = $loggingConfig['cloudwatch']; - - if (!isset($cloudWatchConfigs['region'])) { - throw new IncompleteCloudWatchConfig('Missing region key-value'); - } - - $awsCredentials = [ - 'region' => $cloudWatchConfigs['region'], - 'version' => $cloudWatchConfigs['version'], - ]; - - if ($cloudWatchConfigs['credentials']['key']) { - $awsCredentials['credentials'] = $cloudWatchConfigs['credentials']; - } - - return $awsCredentials; - } - - /** - * @return mixed|LineFormatter - * - * @throws IncompleteCloudWatchConfig - * @throws \Illuminate\Contracts\Container\BindingResolutionException - */ - private function resolveFormatter(array $configs) - { - if (!isset($configs['formatter'])) { - return new LineFormatter( - '%channel%: %level_name%: %message% %context% %extra%', - null, - false, - true - ); - } - - $formatter = $configs['formatter']; - - if (\is_string($formatter) && class_exists($formatter)) { - return $this->app->make($formatter); - } - - if (\is_callable($formatter)) { - return $formatter($configs); - } - - throw new IncompleteCloudWatchConfig('Formatter is missing for the logs'); - } } From b062a97d2fcd7fac84c31d828f9a3e694a3b28f0 Mon Sep 17 00:00:00 2001 From: Naren Date: Mon, 20 Jul 2020 21:06:31 +0400 Subject: [PATCH 2/7] fixed tests --- config/logging.php | 3 +- src/Logger.php | 7 ++- src/Providers/CloudWatchServiceProvider.php | 56 +------------------ ...ServiceProviderTest.php => LoggerTest.php} | 28 +++++----- 4 files changed, 24 insertions(+), 70 deletions(-) rename tests/{Providers/CloudWatchServiceProviderTest.php => LoggerTest.php} (92%) diff --git a/config/logging.php b/config/logging.php index d1e6a91..73ac612 100644 --- a/config/logging.php +++ b/config/logging.php @@ -1,5 +1,4 @@ [ @@ -23,6 +22,6 @@ true ); }, - 'via' => Logger::class + 'via' => \Pagevamp\Logger::class ], ]; diff --git a/src/Logger.php b/src/Logger.php index f26b9e0..c9dc4f2 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -11,13 +11,18 @@ class Logger { private $app; - public function __construct($app) + + public function __construct($app = null) { $this->app = $app; } public function __invoke(array $config) { + if($this->app === null) { + $this->app = \app(); + } + $loggingConfig = $config; $cwClient = new CloudWatchLogsClient($this->getCredentials()); diff --git a/src/Providers/CloudWatchServiceProvider.php b/src/Providers/CloudWatchServiceProvider.php index 12e3a68..2c2ac7d 100644 --- a/src/Providers/CloudWatchServiceProvider.php +++ b/src/Providers/CloudWatchServiceProvider.php @@ -11,58 +11,8 @@ class CloudWatchServiceProvider extends ServiceProvider { public function boot() { - $isCloudWatchDisabled = $this->app->make('config')->get('logging.channels.cloudwatch.disabled'); - if (!$isCloudWatchDisabled) { - $app = $this->app; - $app['log']->listen(function () use ($app) { - $args = \func_get_args(); - - // Laravel 5.4 returns a MessageLogged instance only - if (1 == \count($args)) { - $level = $args[0]->level; - $message = $args[0]->message; - $context = $args[0]->context; - } else { - $level = $args[0]; - $message = $args[1]; - $context = $args[2]; - } - - if ($message instanceof \ErrorException) { - return $this->getLogger()->log($level, $message, $context); - } - - if ($app['cloudwatch.logger'] instanceof Logger) { - $app['cloudwatch.logger']->log($level, $message, $context); - } - }); - } - } - - public function getLogger() - { - $logger = new \Pagevamp\Logger($this->app); - $loggingConfig = $this->app->make('config')->get('logging.channels.cloudwatch'); - return $logger($loggingConfig); + $this->publishes([ + __DIR__.'/../config/logging.php' => config_path('logging.php') + ], 'config'); } - - /** - * Code "inspired" from here - * https://aws.amazon.com/blogs/developer/php-application-logging-with-amazon-cloudwatch-logs-and-monolog - * Laravel installation mentioned here did not work but PHP with Monolog worked, hence this package. - */ - public function register() - { - $this->mergeConfigFrom( - __DIR__.'/../../config/logging.php', - 'logging.channels' - ); - - if (!$this->app->make('config')->get('logging.channels.cloudwatch.disabled')) { - $this->app->singleton('cloudwatch.logger', function () { - return $this->getLogger(); - }); - } - } - } diff --git a/tests/Providers/CloudWatchServiceProviderTest.php b/tests/LoggerTest.php similarity index 92% rename from tests/Providers/CloudWatchServiceProviderTest.php rename to tests/LoggerTest.php index ab61471..b986e37 100644 --- a/tests/Providers/CloudWatchServiceProviderTest.php +++ b/tests/LoggerTest.php @@ -1,20 +1,20 @@ with(JsonFormatter::class) ->andReturn($formatter); - $provider = new CloudWatchServiceProvider($app); - $logger = $provider->getLogger(); + $provider = new \Pagevamp\Logger($app); + $logger = $provider($cloudwatchConfigs); $this->assertInstanceOf(Logger::class, $logger); $this->assertNotEmpty($logger->getHandlers()); @@ -106,8 +106,8 @@ public function testGetLoggerShouldResolveDefaultFormatterInstanceWhenConfigIsNu ->with(JsonFormatter::class) ->andReturn($formatter); - $provider = new CloudWatchServiceProvider($app); - $logger = $provider->getLogger(); + $provider = new \Pagevamp\Logger($app); + $logger = $provider($cloudwatchConfigs); $this->assertInstanceOf(Logger::class, $logger); $this->assertNotEmpty($logger->getHandlers()); @@ -156,8 +156,8 @@ public function testGetLoggerShouldResolveDefaultFormatterInstanceWhenConfigIsNo ->with(LineFormatter::class) ->andReturn($formatter); - $provider = new CloudWatchServiceProvider($app); - $logger = $provider->getLogger(); + $provider = new \Pagevamp\Logger($app); + $logger = $provider($cloudwatchConfigs); $this->assertInstanceOf(Logger::class, $logger); $this->assertNotEmpty($logger->getHandlers()); @@ -209,8 +209,8 @@ public function testGetLoggerShouldResolveCallableFormatter() ->with(LogglyFormatter::class) ->andReturn($formatter); - $provider = new CloudWatchServiceProvider($app); - $logger = $provider->getLogger(); + $provider = new \Pagevamp\Logger($app); + $logger = $provider($cloudwatchConfigs); $this->assertInstanceOf(Logger::class, $logger); $this->assertNotEmpty($logger->getHandlers()); @@ -255,7 +255,7 @@ public function testInvalidFormatterWillThrowException() ->andReturn($config); $this->expectException(IncompleteCloudWatchConfig::class); - $provider = new CloudWatchServiceProvider($app); - $provider->getLogger(); + $provider = new \Pagevamp\Logger($app); + $provider($cloudwatchConfigs); } } From 6d35ef6bc3ed9615feaf2241ca8b8f8b8783f6e7 Mon Sep 17 00:00:00 2001 From: Naren Date: Mon, 20 Jul 2020 21:29:12 +0400 Subject: [PATCH 3/7] removed service provider --- src/Providers/CloudWatchServiceProvider.php | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 src/Providers/CloudWatchServiceProvider.php diff --git a/src/Providers/CloudWatchServiceProvider.php b/src/Providers/CloudWatchServiceProvider.php deleted file mode 100644 index 2c2ac7d..0000000 --- a/src/Providers/CloudWatchServiceProvider.php +++ /dev/null @@ -1,18 +0,0 @@ -publishes([ - __DIR__.'/../config/logging.php' => config_path('logging.php') - ], 'config'); - } -} From 63954b4c529fd299c597d604e7ef039e62d0c725 Mon Sep 17 00:00:00 2001 From: Naren Date: Mon, 20 Jul 2020 21:31:00 +0400 Subject: [PATCH 4/7] removed autodiscovery --- composer.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/composer.json b/composer.json index 98b11b1..0b701c8 100644 --- a/composer.json +++ b/composer.json @@ -23,13 +23,6 @@ "maxbanton/cwh": "^1.1.14 || ^2.0", "illuminate/support": "^5.1 || ^6.0 || ^7.0" }, - "extra": { - "laravel": { - "providers": [ - "Pagevamp\\Providers\\CloudWatchServiceProvider" - ] - } - }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.12 || ^2.16", "phpunit/phpunit": "^6.5 || ^8.4 || ^9.0", From 2f55bafba8c88720385d755e7a2e02613c7233f8 Mon Sep 17 00:00:00 2001 From: Naren Date: Wed, 28 Jul 2021 14:00:07 +0545 Subject: [PATCH 5/7] updated readme --- readme.md | 30 +++++++++--------------------- src/Logger.php | 2 ++ 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/readme.md b/readme.md index 72480a8..4e1e5f3 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,11 @@ ## Logger for Aws Cloud Watch +### Breaking Change for version 1.0 + +When this package started it started as a listener for log event and would only work with another driver. +This package would listen to log events and just log extra to cloud watch but after `1.0` it works as a custom driver. +So, you MUST add a `LOG_CHANNEL` for logging in your config for this to work. + ### Installation `composer require pagevamp/laravel-cloudwatch-logs` @@ -28,34 +34,16 @@ Config for logging is defined at `config/logging.php`. Add `cloudwatch` to the ` 'retention' => env('CLOUDWATCH_LOG_RETENTION_DAYS', 14), 'group_name' => env('CLOUDWATCH_LOG_GROUP_NAME', 'laravel_app'), 'version' => env('CLOUDWATCH_LOG_VERSION', 'latest'), - 'formatter' => \Monolog\Formatter\JsonFormatter::class, - 'disabled' => env('DISABLE_CLOUDWATCH_LOG', false), + 'formatter' => \Monolog\Formatter\JsonFormatter::class, + 'via' => \Pagevamp\Logger::class, ], ] ``` -Add correct values to keys in your `.env` file. And it should work. +And set the log channel in your environment variable to `LOG_CHANNEL` to `cloudwatch`. If the role of your AWS EC2 instance has access to Cloudwatch logs, `CLOUDWATCH_LOG_KEY` and `CLOUDWATCH_LOG_SECRET` need not be defined in your `.env` file. -### Add To Project - -#### Laravel 5.5 or Higher - -This package uses laravel's [Package discovery](https://laravel.com/docs/5.6/packages#package-discovery). To disable this package by default you can add `DISABLE_CLOUDWATCH_LOG=true` to you local `.env` file and this package will be disabled. - -#### Laravel 5.4 or Lower - -Add to the `providers` array in `config/app.php`: - -``` -Pagevamp\Providers\CloudWatchServiceProvider::class -``` - -### Concept - -This package relies on laravel's listener for log events. This package DOES NOT replace the default logging, instead adds additional log to AWS CLoud Watch. Hence you do not have to change the default log driver to make this work. - ### Contribution I have added a `pre-commit` hook to run `php-cs-fixer` whenever you make a commit. To enable this run `sh hooks.sh`. diff --git a/src/Logger.php b/src/Logger.php index c9dc4f2..2143ea7 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -46,6 +46,7 @@ public function __invoke(array $config) * in key cloudwatch. * * 'cloudwatch' => [ + * 'driver' => 'custom', * 'name' => env('CLOUDWATCH_LOG_NAME', ''), * 'region' => env('CLOUDWATCH_LOG_REGION', ''), * 'credentials' => [ @@ -56,6 +57,7 @@ public function __invoke(array $config) * 'retention' => env('CLOUDWATCH_LOG_RETENTION_DAYS', 14), * 'group_name' => env('CLOUDWATCH_LOG_GROUP_NAME', 'laravel_app'), * 'version' => env('CLOUDWATCH_LOG_VERSION', 'latest'), + * 'via' => \Pagevamp\Logger::class, * ] * * @return array From 76635ed2751297a46ebbe1c8c126398cbde3802b Mon Sep 17 00:00:00 2001 From: Naren Date: Wed, 28 Jul 2021 14:35:52 +0545 Subject: [PATCH 6/7] fixed readme --- config/logging.php | 1 + readme.md | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/config/logging.php b/config/logging.php index 73ac612..1670f08 100644 --- a/config/logging.php +++ b/config/logging.php @@ -14,6 +14,7 @@ 'group_name' => env('CLOUDWATCH_LOG_GROUP_NAME', 'laravel_app'), 'version' => env('CLOUDWATCH_LOG_VERSION', 'latest'), 'disabled' => env('DISABLE_CLOUDWATCH_LOG', false), + 'batch_size' => env('CLOUDWATCH_LOG_BATCH_SIZE', 10000), 'formatter' => function ($configs) { return new \Monolog\Formatter\LineFormatter( '%channel%: %level_name%: %message% %context% %extra%', diff --git a/readme.md b/readme.md index 4e1e5f3..2897334 100644 --- a/readme.md +++ b/readme.md @@ -2,9 +2,10 @@ ### Breaking Change for version 1.0 -When this package started it started as a listener for log event and would only work with another driver. -This package would listen to log events and just log extra to cloud watch but after `1.0` it works as a custom driver. -So, you MUST add a `LOG_CHANNEL` for logging in your config for this to work. +When this package started it started as a listener for log event and would only work with another channel. +This package would listen to log events and just add extra log to cloud watch. So you did not need to add mention this as `channel`. +But after `1.0` it works as a custom driver. +So, you MUST add a `LOG_CHANNEL` as `cloudwatch` for logging in your config for this to work going forward ### Installation @@ -34,13 +35,14 @@ Config for logging is defined at `config/logging.php`. Add `cloudwatch` to the ` 'retention' => env('CLOUDWATCH_LOG_RETENTION_DAYS', 14), 'group_name' => env('CLOUDWATCH_LOG_GROUP_NAME', 'laravel_app'), 'version' => env('CLOUDWATCH_LOG_VERSION', 'latest'), - 'formatter' => \Monolog\Formatter\JsonFormatter::class, + 'formatter' => \Monolog\Formatter\JsonFormatter::class, + 'batch_size' => env('CLOUDWATCH_LOG_BATCH_SIZE', 10000), 'via' => \Pagevamp\Logger::class, ], ] ``` -And set the log channel in your environment variable to `LOG_CHANNEL` to `cloudwatch`. +And set the `LOG_CHANNEL` in your environment variable to `cloudwatch`. If the role of your AWS EC2 instance has access to Cloudwatch logs, `CLOUDWATCH_LOG_KEY` and `CLOUDWATCH_LOG_SECRET` need not be defined in your `.env` file. From 7841adeae4cc884856805cc3024ad09909dc5ee0 Mon Sep 17 00:00:00 2001 From: Naren Date: Thu, 29 Jul 2021 15:12:47 +0545 Subject: [PATCH 7/7] removed disabled from config --- config/logging.php | 1 - src/Logger.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/config/logging.php b/config/logging.php index 1670f08..f12cfb9 100644 --- a/config/logging.php +++ b/config/logging.php @@ -13,7 +13,6 @@ 'retention' => env('CLOUDWATCH_LOG_RETENTION_DAYS', 14), 'group_name' => env('CLOUDWATCH_LOG_GROUP_NAME', 'laravel_app'), 'version' => env('CLOUDWATCH_LOG_VERSION', 'latest'), - 'disabled' => env('DISABLE_CLOUDWATCH_LOG', false), 'batch_size' => env('CLOUDWATCH_LOG_BATCH_SIZE', 10000), 'formatter' => function ($configs) { return new \Monolog\Formatter\LineFormatter( diff --git a/src/Logger.php b/src/Logger.php index 2143ea7..f1dcd9f 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -57,7 +57,7 @@ public function __invoke(array $config) * 'retention' => env('CLOUDWATCH_LOG_RETENTION_DAYS', 14), * 'group_name' => env('CLOUDWATCH_LOG_GROUP_NAME', 'laravel_app'), * 'version' => env('CLOUDWATCH_LOG_VERSION', 'latest'), - * 'via' => \Pagevamp\Logger::class, + * 'via' => \Pagevamp\Logger::class, * ] * * @return array