Просмотр исходного кода

Custom exception handler (#500)

Add custom exception handler to let the user change the default
behavior when Monolog raise an exception while logging a record.
Vincent Paré 8 лет назад
Родитель
Сommit
207c91699e
2 измененных файлов с 126 добавлено и 8 удалено
  1. 57 8
      src/Monolog/Logger.php
  2. 69 0
      tests/Monolog/LoggerTest.php

+ 57 - 8
src/Monolog/Logger.php

@@ -133,6 +133,11 @@ class Logger implements LoggerInterface
      */
     protected $microsecondTimestamps = true;
 
+    /**
+     * @var callable
+     */
+    protected $exceptionHandler;
+
     /**
      * @param string             $name       The logging channel
      * @param HandlerInterface[] $handlers   Optional stack of handlers, the first one in the array is called first, etc.
@@ -329,16 +334,20 @@ class Logger implements LoggerInterface
             'extra' => array(),
         );
 
-        foreach ($this->processors as $processor) {
-            $record = call_user_func($processor, $record);
-        }
-
-        while ($handler = current($this->handlers)) {
-            if (true === $handler->handle($record)) {
-                break;
+        try {
+            foreach ($this->processors as $processor) {
+                $record = call_user_func($processor, $record);
             }
 
-            next($this->handlers);
+            while ($handler = current($this->handlers)) {
+                if (true === $handler->handle($record)) {
+                    break;
+                }
+
+                next($this->handlers);
+            }
+        } catch (\Exception $ex) {
+            $this->handleException($ex, $record);
         }
 
         return true;
@@ -501,6 +510,46 @@ class Logger implements LoggerInterface
         return false;
     }
 
+    /**
+     * Set a custom exception handler
+     *
+     * @param  callable $callback
+     * @return $this
+     */
+    public function setExceptionHandler($callback)
+    {
+        if (!is_callable($callback)) {
+            throw new \InvalidArgumentException('Exception handler must be valid callable (callback or object with an __invoke method), '.var_export($callback, true).' given');
+        }
+        $this->exceptionHandler = $callback;
+
+        return $this;
+    }
+
+    /**
+     * @return callable
+     */
+    public function getExceptionHandler()
+    {
+        return $this->exceptionHandler;
+    }
+
+    /**
+     * Delegates exception management to the custom exception handler,
+     * or throws the exception if no custom handler is set.
+     *
+     * @param Exception $ex
+     * @param array $record
+     */
+    protected function handleException(\Exception $ex, $record)
+    {
+        if ($this->exceptionHandler) {
+            call_user_func($this->exceptionHandler, $ex, $record);
+        } else {
+            throw $ex;
+        }
+    }
+
     /**
      * Adds a log record at an arbitrary level.
      *

+ 69 - 0
tests/Monolog/LoggerTest.php

@@ -545,4 +545,73 @@ class LoggerTest extends \PHPUnit_Framework_TestCase
             'without microseconds' => array(false, PHP_VERSION_ID >= 70100 ? 'assertNotSame' : 'assertSame'),
         );
     }
+
+    /**
+     * @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::setExceptionHandler
+     * @expectedException InvalidArgumentException
+     */
+    public function testBadExceptionHandlerType()
+    {
+        $logger = new Logger(__METHOD__);
+        $logger->setExceptionHandler(false);
+    }
+
+    /**
+     * @covers Monolog\Logger::handleException
+     * @expectedException Exception
+     */
+    public function testDefaultHandleException()
+    {
+        $logger = new Logger(__METHOD__);
+        $handler = $this->getMock('Monolog\Handler\HandlerInterface');
+        $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->getMock('Monolog\Handler\HandlerInterface');
+        $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');
+    }
 }