mirror of
https://github.com/Seldaek/monolog.git
synced 2025-07-31 18:30:15 +02:00
Recursively check for resource arguments if trace is coming from internal function.
With `React` or `Guzzle`, that register stream wrappers with PHP, the traces are treated as coming from internal functions with no line and file inside the frame. But they almost always contain resources as arguments, on which the `json_encode()` call will choke (probably this should be addressed in json_encode internally, since it is very easy to cast a resource to a string). I added a test case proving the situation and a pretty basic recursive checker for resources which just casts them as a string into the frame again.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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');
|
||||
|
Reference in New Issue
Block a user