Jordi Boggiano 7 rokov pred
rodič
commit
12c9f58de2

+ 1 - 1
src/Monolog/Handler/RavenHandler.php

@@ -181,7 +181,7 @@ class RavenHandler extends AbstractProcessingHandler
         }
 
         if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) {
-            $options['extra']['message'] = $record['formatted'];
+            $options['message'] = $record['formatted'];
             $this->ravenClient->captureException($record['context']['exception'], $options);
         } else {
             $this->ravenClient->captureMessage($record['formatted'], [], $options);

+ 10 - 0
src/Monolog/Handler/WhatFailureGroupHandler.php

@@ -46,6 +46,16 @@ class WhatFailureGroupHandler extends GroupHandler
      */
     public function handleBatch(array $records)
     {
+        if ($this->processors) {
+            $processed = array();
+            foreach ($records as $record) {
+                foreach ($this->processors as $processor) {
+                    $processed[] = call_user_func($processor, $record);
+                }
+            }
+            $records = $processed;
+        }
+
         foreach ($this->handlers as $handler) {
             try {
                 $handler->handleBatch($records);

+ 53 - 13
src/Monolog/Logger.php

@@ -15,6 +15,7 @@ use DateTimeZone;
 use Monolog\Handler\HandlerInterface;
 use Psr\Log\LoggerInterface;
 use Psr\Log\InvalidArgumentException;
+use Throwable;
 
 /**
  * Monolog log channel
@@ -135,6 +136,11 @@ class Logger implements LoggerInterface
      */
     protected $timezone;
 
+    /**
+     * @var callable
+     */
+    protected $exceptionHandler;
+
     /**
      * @param string             $name       The logging channel, a simple descriptive name that is attached to all log records
      * @param HandlerInterface[] $handlers   Optional stack of handlers, the first one in the array is called first, etc.
@@ -304,22 +310,26 @@ class Logger implements LoggerInterface
             'extra' => [],
         ];
 
-        foreach ($this->processors as $processor) {
-            $record = call_user_func($processor, $record);
-        }
-
-        // advance the array pointer to the first handler that will handle this record
-        reset($this->handlers);
-        while ($handlerKey !== key($this->handlers)) {
-            next($this->handlers);
-        }
+        try {
+            foreach ($this->processors as $processor) {
+                $record = call_user_func($processor, $record);
+            }
 
-        while ($handler = current($this->handlers)) {
-            if (true === $handler->handle($record)) {
-                break;
+            // advance the array pointer to the first handler that will handle this record
+            reset($this->handlers);
+            while ($handlerKey !== key($this->handlers)) {
+                next($this->handlers);
             }
 
-            next($this->handlers);
+            while ($handler = current($this->handlers)) {
+                if (true === $handler->handle($record)) {
+                    break;
+                }
+
+                next($this->handlers);
+            }
+        } catch (Throwable $e) {
+            $this->handleException($e, $record);
         }
 
         return true;
@@ -392,6 +402,23 @@ class Logger implements LoggerInterface
         return false;
     }
 
+    /**
+     * Set a custom exception handler that will be called if adding a new record fails
+     *
+     * The callable will receive an exception object and the record that failed to be logged
+     */
+    public function setExceptionHandler(?callable $callback): self
+    {
+        $this->exceptionHandler = $callback;
+
+        return $this;
+    }
+
+    public function getExceptionHandler(): ?callable
+    {
+        return $this->exceptionHandler;
+    }
+
     /**
      * Adds a log record at an arbitrary level.
      *
@@ -533,4 +560,17 @@ class Logger implements LoggerInterface
     {
         return $this->timezone;
     }
+
+    /**
+     * Delegates exception management to the custom exception handler,
+     * or throws the exception if no custom handler is set.
+     */
+    protected function handleException(Throwable $e, array $record)
+    {
+        if (!$this->exceptionHandler) {
+            throw $e;
+        }
+
+        call_user_func($this->exceptionHandler, $e, $record);
+    }
 }

+ 23 - 0
tests/Monolog/Handler/WhatFailureGroupHandlerTest.php

@@ -87,6 +87,29 @@ class WhatFailureGroupHandlerTest extends TestCase
         $this->assertTrue($records[0]['extra']['foo']);
     }
 
+    /**
+     * @covers Monolog\Handler\WhatFailureGroupHandler::handleBatch
+     */
+    public function testHandleBatchUsesProcessors()
+    {
+        $testHandlers = array(new TestHandler(), new TestHandler());
+        $handler = new WhatFailureGroupHandler($testHandlers);
+        $handler->pushProcessor(function ($record) {
+            $record['extra']['foo'] = true;
+
+            return $record;
+        });
+        $handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO)));
+        foreach ($testHandlers as $test) {
+            $this->assertTrue($test->hasDebugRecords());
+            $this->assertTrue($test->hasInfoRecords());
+            $this->assertTrue(count($test->getRecords()) === 2);
+            $records = $test->getRecords();
+            $this->assertTrue($records[0]['extra']['foo']);
+            $this->assertTrue($records[1]['extra']['foo']);
+        }
+    }
+
     /**
      * @covers Monolog\Handler\WhatFailureGroupHandler::handle
      */

+ 59 - 0
tests/Monolog/LoggerTest.php

@@ -576,4 +576,63 @@ class LoggerTest extends \PHPUnit\Framework\TestCase
             'without microseconds' => [false, PHP_VERSION_ID >= 70100 ? 'assertNotSame' : 'assertSame', 'Y-m-d\TH:i:sP'],
         ];
     }
+
+    /**
+     * @covers Monolog\Logger::setExceptionHandler
+     */
+    public function testSetExceptionHandler()
+    {
+        $logger = new Logger(__METHOD__);
+        $this->assertNull($logger->getExceptionHandler());
+        $callback = function ($ex) {
+        };
+        $logger->setExceptionHandler($callback);
+        $this->assertEquals($callback, $logger->getExceptionHandler());
+    }
+
+    /**
+     * @covers Monolog\Logger::handleException
+     * @expectedException Exception
+     */
+    public function testDefaultHandleException()
+    {
+        $logger = new Logger(__METHOD__);
+        $handler = $this->getMockBuilder('Monolog\Handler\HandlerInterface')->getMock();
+        $handler->expects($this->any())
+            ->method('isHandling')
+            ->will($this->returnValue(true))
+        ;
+        $handler->expects($this->any())
+            ->method('handle')
+            ->will($this->throwException(new \Exception('Some handler exception')))
+        ;
+        $logger->pushHandler($handler);
+        $logger->info('test');
+    }
+
+    /**
+     * @covers Monolog\Logger::handleException
+     * @covers Monolog\Logger::addRecord
+     */
+    public function testCustomHandleException()
+    {
+        $logger = new Logger(__METHOD__);
+        $that = $this;
+        $logger->setExceptionHandler(function ($e, $record) use ($that) {
+            $that->assertEquals($e->getMessage(), 'Some handler exception');
+            $that->assertTrue(is_array($record));
+            $that->assertEquals($record['message'], 'test');
+        });
+        $handler = $this->getMockBuilder('Monolog\Handler\HandlerInterface')->getMock();
+        $handler->expects($this->any())
+            ->method('isHandling')
+            ->will($this->returnValue(true))
+        ;
+        $handler->expects($this->any())
+            ->method('handle')
+            ->will($this->throwException(new \Exception('Some handler exception')))
+        ;
+        $logger->pushHandler($handler);
+        $logger->info('test');
+    }
 }