From 0ce72d7c9cbe0b751f2c362851ffa9b8688c7cc7 Mon Sep 17 00:00:00 2001 From: Aivis Silins Date: Tue, 12 Apr 2022 10:33:51 +0300 Subject: [PATCH 1/3] Add log filter --- .../UnderstandLaravel5ServiceProvider.php | 23 ++-- src/config/understand-laravel.php | 19 +++ tests/LogFilterTest.php | 109 ++++++++++++++++++ 3 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 tests/LogFilterTest.php diff --git a/src/Understand/UnderstandLaravel5/UnderstandLaravel5ServiceProvider.php b/src/Understand/UnderstandLaravel5/UnderstandLaravel5ServiceProvider.php index 6b948f1..371bee9 100644 --- a/src/Understand/UnderstandLaravel5/UnderstandLaravel5ServiceProvider.php +++ b/src/Understand/UnderstandLaravel5/UnderstandLaravel5ServiceProvider.php @@ -434,22 +434,27 @@ protected function handleEvent($level, $message, $context) } } - /** - * @param $level - * @param $message - * @param $context - * @return bool - */ protected function shouldIgnoreEvent($level, $message, $context) { $ignoredEventTypes = (array)$this->app['config']->get('understand-laravel.ignored_logs'); + $logFilter = $this->app['config']->get('understand-laravel.log_filter'); + + // check if the log should be ignored by its level (info, warning, etc.) + if ($ignoredEventTypes) + { + return in_array($level, $ignoredEventTypes, true); + } - if ( ! $ignoredEventTypes) + // check if a custom filter is set and whether the log should be ignored + // true - the log should be ignored + // false - the log should be delivered to Understand + if (is_callable($logFilter)) { - return false; + return (bool)$logFilter($level, $message, $context); } - return in_array($level, $ignoredEventTypes, true); + // by default logs are not ignored + return false; } /** diff --git a/src/config/understand-laravel.php b/src/config/understand-laravel.php index aa082d1..7e2a21b 100644 --- a/src/config/understand-laravel.php +++ b/src/config/understand-laravel.php @@ -66,6 +66,25 @@ //'emergency', ], + /** + * Log filter. + * + * The configuration value (filter) must be a callable type: + * - https://www.php.net/manual/en/function.is-callable.php + * + * The suggested way would be to create an invokable class since it's hard to serialise anonymous functions (Laravel config cache): + * - https://www.php.net/manual/en/language.oop5.magic.php#object.invoke + * + * The log (callable) filter interface is as follows: `$callable($level, $message, $context)`. + * + * The result of the filter must be a boolean value: + * - TRUE, the log should be ignored and NOT delivered to Understand.io + * - FALSE, the log should be delivered to Understand.io + * + * The `ignored_logs` config value has higher precedence than `log_filter`. + */ + 'log_filter' => null, + /** * Field names which values should not be sent to Understand.io * It applies to POST and GET request parameters diff --git a/tests/LogFilterTest.php b/tests/LogFilterTest.php new file mode 100644 index 0000000..4d3b43b --- /dev/null +++ b/tests/LogFilterTest.php @@ -0,0 +1,109 @@ +app['config']->set('understand-laravel.log_filter', function() { + // FALSE, logs should not be filtered + return false; + }); + + $handler = new CallbackHandler($callback); + $this->app['understand.logger'] = new Logger($this->app['understand.fieldProvider'], $handler); + + // trigger error + $this->app['Psr\Log\LoggerInterface']->error('test'); + $this->app['Psr\Log\LoggerInterface']->warning('test2'); + + $this->assertEquals(2, $logsSent); + } + + /** + * @return void + */ + public function testLogFilterFiltersOneLog() + { + $logsSent = 0; + + $callback = function() use(&$logsSent) + { + $logsSent++; + }; + + $this->app['config']->set('understand-laravel.log_filter', function($level, $message, $context) { + if ($message === 'test2') { + // TRUE, log should be filtered + return true; + } + + // FALSE, logs should not be filtered + return false; + }); + + $handler = new CallbackHandler($callback); + $this->app['understand.logger'] = new Logger($this->app['understand.fieldProvider'], $handler); + + // trigger error + $this->app['Psr\Log\LoggerInterface']->error('test'); + $this->app['Psr\Log\LoggerInterface']->warning('test2'); + + $this->assertEquals(1, $logsSent); + } + + /** + * @return void + */ + public function testLogFilterReceivesAllData() + { + $logsSent = 0; + + $callback = function() use(&$logsSent) + { + $logsSent++; + }; + + $this->app['config']->set('understand-laravel.log_filter', function($level, $message, $context) { + $this->assertEquals('error', $level); + $this->assertEquals('test', $message); + $this->assertEquals(['context' => 'value'], $context); + + // FALSE, logs should not be filtered + return false; + }); + + $handler = new CallbackHandler($callback); + $this->app['understand.logger'] = new Logger($this->app['understand.fieldProvider'], $handler); + + // trigger error + $this->app['Psr\Log\LoggerInterface']->error('test', ['context' => 'value']); + + $this->assertEquals(1, $logsSent); + } +} \ No newline at end of file From c424192dd2ce18a00bc9e24c4106187df2b02d97 Mon Sep 17 00:00:00 2001 From: Aivis Silins Date: Tue, 12 Apr 2022 10:58:10 +0300 Subject: [PATCH 2/3] add the service container resolver --- .../UnderstandLaravel5ServiceProvider.php | 6 ++-- src/config/understand-laravel.php | 2 ++ tests/LogFilterTest.php | 35 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Understand/UnderstandLaravel5/UnderstandLaravel5ServiceProvider.php b/src/Understand/UnderstandLaravel5/UnderstandLaravel5ServiceProvider.php index 371bee9..7b0fffe 100644 --- a/src/Understand/UnderstandLaravel5/UnderstandLaravel5ServiceProvider.php +++ b/src/Understand/UnderstandLaravel5/UnderstandLaravel5ServiceProvider.php @@ -448,9 +448,11 @@ protected function shouldIgnoreEvent($level, $message, $context) // check if a custom filter is set and whether the log should be ignored // true - the log should be ignored // false - the log should be delivered to Understand - if (is_callable($logFilter)) + if ($logFilter) { - return (bool)$logFilter($level, $message, $context); + $factory = is_callable($logFilter) ? $logFilter : $this->app->make($logFilter); + + return (bool)$factory($level, $message, $context); } // by default logs are not ignored diff --git a/src/config/understand-laravel.php b/src/config/understand-laravel.php index 7e2a21b..984e022 100644 --- a/src/config/understand-laravel.php +++ b/src/config/understand-laravel.php @@ -71,6 +71,8 @@ * * The configuration value (filter) must be a callable type: * - https://www.php.net/manual/en/function.is-callable.php + * or a callable dependency from the service container: + * - https://laravel.com/docs/9.x/container#the-make-method * * The suggested way would be to create an invokable class since it's hard to serialise anonymous functions (Laravel config cache): * - https://www.php.net/manual/en/language.oop5.magic.php#object.invoke diff --git a/tests/LogFilterTest.php b/tests/LogFilterTest.php index 4d3b43b..abcb323 100644 --- a/tests/LogFilterTest.php +++ b/tests/LogFilterTest.php @@ -45,6 +45,41 @@ public function testLogFilterAllowsDelivery() $this->assertEquals(2, $logsSent); } + /** + * @return void + */ + public function testServiceContainerDependency() + { + $logsSent = 0; + + $callback = function() use(&$logsSent) + { + $logsSent++; + }; + + $dependencyName = 'service-container-dependency'; + $dependencyCalled = false; + + $this->app->bind($dependencyName, function() use(&$dependencyCalled) { + return function() use(&$dependencyCalled) { + $dependencyCalled = true; + // FALSE, logs should not be filtered + return false; + }; + }); + + $this->app['config']->set('understand-laravel.log_filter', $dependencyName); + + $handler = new CallbackHandler($callback); + $this->app['understand.logger'] = new Logger($this->app['understand.fieldProvider'], $handler); + + // trigger error + $this->app['Psr\Log\LoggerInterface']->error('test'); + + $this->assertTrue($dependencyCalled); + $this->assertEquals(1, $logsSent); + } + /** * @return void */ From ce33d8f541ebe4842e6e4d83b8e9196e027910b5 Mon Sep 17 00:00:00 2001 From: Aivis Silins Date: Tue, 12 Apr 2022 11:28:42 +0300 Subject: [PATCH 3/3] update README.md --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/README.md b/README.md index 2bd7819..571a9d0 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,56 @@ Laravel's (`>= 5.0, < 5.1`) exception logger doesn't use event dispatcher (https php artisan vendor:publish --provider="Understand\UnderstandLaravel5\UnderstandLaravel5ServiceProvider" ``` +### Log Filter +To filter out specific log types a custom log filter can be provided. + +**Example filter class** +```php +// app/Logging/UnderstandLogFilter.php + \App\Logging\UnderstandLogFilter::class, +``` + +The `log_filter` config value must be a callable type: +- https://www.php.net/manual/en/function.is-callable.php + or a callable dependency from the service container: +- https://laravel.com/docs/9.x/container#the-make-method + +The suggested way would be to create an invokable class since it's hard to serialise anonymous functions (Laravel config cache): +- https://www.php.net/manual/en/language.oop5.magic.php#object.invoke + +The log filter interface must be as follows: `$callable($level, $message, $context)`. +The result of the filter must be a boolean value: +- `TRUE`, the log should be ignored and NOT delivered to Understand.io +- `FALSE`, the log should be delivered to Understand.io + +The `ignored_logs` config value has higher precedence than `log_filter`. + + ### Requirements ##### UTF-8 This package uses the json_encode function, which only supports UTF-8 data, and you should therefore ensure that all of your data is correctly encoded. In the event that your log data contains non UTF-8 strings, then the json_encode function will not be able to serialize the data.