diff --git a/src/Monolog/Formatter/NormalizerFormatter.php b/src/Monolog/Formatter/NormalizerFormatter.php index 95b3de36..080e1ab0 100644 --- a/src/Monolog/Formatter/NormalizerFormatter.php +++ b/src/Monolog/Formatter/NormalizerFormatter.php @@ -108,6 +108,7 @@ class NormalizerFormatter implements FormatterInterface if (isset($frame['file'])) { $data['trace'][] = $frame['file'].':'.$frame['line']; } else { + $this->convertResourceArgs($frame); $data['trace'][] = json_encode($frame); } } @@ -136,4 +137,26 @@ class NormalizerFormatter implements FormatterInterface return json_encode($data); } + + /** + * This method checks recursively for resource args inside the frame, since json_encode is choking on them. + * + * @param array &$frame Reference to current frame + */ + private function convertResourceArgs(array &$frame) + { + foreach ($frame as $key => &$item) { + if (is_scalar($item)) { + continue; + } + + if (is_resource($item)) { + $frame[$key] = (string) $item; + } + + if (is_array($item)) { + $this->convertResourceArgs($item); + } + } + } } diff --git a/tests/Monolog/Formatter/GelfMessageFormatterTest.php b/tests/Monolog/Formatter/GelfMessageFormatterTest.php index 3f47a09a..d92a2104 100644 --- a/tests/Monolog/Formatter/GelfMessageFormatterTest.php +++ b/tests/Monolog/Formatter/GelfMessageFormatterTest.php @@ -182,6 +182,46 @@ class GelfMessageFormatterTest extends \PHPUnit_Framework_TestCase $this->assertEquals('pair', $message_array['_EXTkey']); } + /** + * @covers Monolog\Formatter\GelfMessageFormatter::format + */ + public function testExceptionObjectWithResourceTrace() + { + // This happens i.e. in React promises or Guzzle streams where stream wrappers are registered + // and no file or line are included in the trace because it's treated as internal function + set_error_handler(function ($errno, $errstr, $errfile, $errline ) { + throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); + }); + + try { + $resource = fopen('php://memory', '+w'); + // Just do something stupid with a resource as argument + strpos($resource); + } catch (\Exception $e) { + } + + // restore the error handler + restore_error_handler(); + + $formatter = new GelfMessageFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger', 'exception' => $e), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log' + ); + + $message = $formatter->format($record); + + $this->assertRegExp( + '%\\\\"resource\\\\":\\\\"Resource id #\d+\\\\"%', + $message->getAdditional('ctxt_exception') + ); + } + private function isLegacy() { return interface_exists('\Gelf\IMessagePublisher');