From b3df450d3d8308c8818ce9e8e62cc6a46da5eab6 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 20 Oct 2022 16:51:21 -0700 Subject: [PATCH] use Reflection to log Exception properties --- phpstan.neon | 1 + src/API/class-bh-wp-psr-logger.php | 16 +++++++++++++--- .../API/class-bh-wp-psr-logger-unit-Test.php | 12 ++++++++---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 5ce5d53..bc38d34 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -15,3 +15,4 @@ parameters: - '#LoggerInterface\|null#' - '#Function apply_filters invoked with \d+ parameters, \d+ required.#' - '#Parameter \#2 \$callback of static method WP_Mock::expect.*Added\(\) expects callable\(\): mixed, array.*given\.#' + - '#Variable \$log_data in empty\(\) always exists and is not falsy#' diff --git a/src/API/class-bh-wp-psr-logger.php b/src/API/class-bh-wp-psr-logger.php index 870d390..5adadd8 100644 --- a/src/API/class-bh-wp-psr-logger.php +++ b/src/API/class-bh-wp-psr-logger.php @@ -34,6 +34,10 @@ public function get_logger(): LoggerInterface { * When an error is being logged, record the time of the last error, so later, an admin notice can be displayed, * to inform them of the new problem. * + * TODO: This always displays the admin notice even when the log itself is filtered. i.e. this function runs before + * the filter, so the code needs to be moved. + * TODO: Allow configuring which log levels result in the admin notice. + * * TODO: include a link to the log url so the last file with an error will be linked, rather than the most recent log file. * * @param string $message The message to be logged. @@ -86,7 +90,7 @@ public function log( $level, $message, $context = array() ) { $context['filters'] = $wp_current_filter; } elseif ( LogLevel::WARNING === $level || LogLevel::DEBUG === $settings_log_level ) { - $debug_backtrace = $this->get_backtrace( 2 ); + $debug_backtrace = $this->get_backtrace( 3 ); $context['debug_backtrace'] = $debug_backtrace; global $wp_current_filter; @@ -99,7 +103,13 @@ public function log( $level, $message, $context = array() ) { $exception_details['class'] = get_class( $exception ); $exception_details['message'] = $exception->getMessage(); - // TODO: Use reflection to log properties of Exception subclasses. + $reflect = new \ReflectionClass( get_class( $exception ) ); + $props = array(); + foreach ( $reflect->getProperties() as $property ) { + $property->setAccessible( true ); + $props[ $property->getName() ] = $property->getValue( $exception ); + } + $exception_details['properties'] = $props; $context['exception'] = $exception_details; } @@ -123,7 +133,7 @@ public function log( $level, $message, $context = array() ) { * Filter to modify the log data. * Return null to cancel logging this message. * - * @pararm array{level:string,message:string,context:array} $log_data + * @param array{level:string,message:string,context:array} $log_data * @param Logger_Settings_Interface $settings * @param BH_WP_PSR_Logger $bh_wp_psr_logger */ diff --git a/tests/unit/API/class-bh-wp-psr-logger-unit-Test.php b/tests/unit/API/class-bh-wp-psr-logger-unit-Test.php index db73213..a97a510 100644 --- a/tests/unit/API/class-bh-wp-psr-logger-unit-Test.php +++ b/tests/unit/API/class-bh-wp-psr-logger-unit-Test.php @@ -20,8 +20,8 @@ protected function tearDown(): void { } /** - * When an exception is passed in the context, it just gets logged as `{}`, so let's instead log the - * exception type and any strings it contains. + * When an exception is passed in the context, it normally just gets logged as `{}`, so let's instead log the + * exception type and message, and use reflection to get its properties. * * @covers ::log */ @@ -32,7 +32,7 @@ public function test_exception(): void { $sut = new BH_WP_PSR_Logger( $settings, $logger ); - $exception = new \Exception( 'Exception message' ); + $exception = new \Exception( 'Exception message', 123 ); \WP_Mock::userFunction( 'update_option' @@ -48,8 +48,12 @@ public function test_exception(): void { $logged_exception = $logger->recordsByLevel['error'][0]['context']['exception']; $this->assertArrayHasKey( 'class', $logged_exception ); - $this->assertArrayHasKey( 'message', $logged_exception ); + $this->assertArrayHasKey( 'message', $logged_exception ); $this->assertEquals( 'Exception message', $logged_exception['message'] ); + + $this->assertArrayHasKey( 'properties', $logged_exception ); + $this->assertEquals( 123, $logged_exception['properties']['code'] ); + } }