1
0
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:
Thomas Ploch
2014-12-16 10:13:12 +01:00
parent a6e82ad0f7
commit 00bfec630a
2 changed files with 63 additions and 0 deletions

View File

@@ -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);
}
}
}
}

View File

@@ -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');