Index: src/main/php/Logger.php =================================================================== --- src/main/php/Logger.php (revision 930537) +++ src/main/php/Logger.php (working copy) @@ -108,7 +108,9 @@ 'LoggerRendererDefault' => '/renderers/LoggerRendererDefault.php', 'LoggerRendererObject' => '/renderers/LoggerRendererObject.php', 'LoggerRendererMap' => '/renderers/LoggerRendererMap.php', + 'LoggerRendererException' => '/renderers/LoggerRendererException.php', 'LoggerLocationInfo' => '/LoggerLocationInfo.php', + 'LoggerThrowableInformation' => '/LoggerThrowableInformation.php', 'LoggerLoggingEvent' => '/LoggerLoggingEvent.php', 'LoggerFilter' => '/LoggerFilter.php', 'LoggerFilterDenyAll' => '/filters/LoggerFilterDenyAll.php', @@ -280,7 +282,12 @@ * @see LoggerLoggingEvent */ public function forcedLog($fqcn, $caller, $level, $message) { - $this->callAppenders(new LoggerLoggingEvent($fqcn, $this, $level, $message)); + $throwable = null; + if ($caller !== null && $caller instanceof Exception) { + $throwable = $caller; + } + + $this->callAppenders(new LoggerLoggingEvent($fqcn, $this, $level, $message, null, $throwable)); } Index: src/main/php/LoggerLoggingEvent.php =================================================================== --- src/main/php/LoggerLoggingEvent.php (revision 930537) +++ src/main/php/LoggerLoggingEvent.php (working copy) @@ -109,6 +109,11 @@ * @var LoggerLocationInfo Location information for the caller. */ private $locationInfo = null; + + /** + * @var LoggerThrowableInformation log4php internal representation of throwable + */ + private $throwableInfo = null; /** * Instantiate a LoggingEvent from the supplied parameters. @@ -121,8 +126,9 @@ * @param LoggerLevel $priority The level of this event. * @param mixed $message The message of this event. * @param integer $timeStamp the timestamp of this logging event. + * @param Exception $throwable The throwable associated with logging event */ - public function __construct($fqcn, $logger, $priority, $message, $timeStamp = null) { + public function __construct($fqcn, $logger, $priority, $message, $timeStamp = null, $throwable = null) { $this->fqcn = $fqcn; if($logger instanceof Logger) { $this->logger = $logger; @@ -142,6 +148,10 @@ $this->timeStamp = floatval(time()); } } + + if ($throwable !== null && $throwable instanceof Exception) { + $this->throwableInfo = new LoggerThrowableInformation($throwable); + } } /** @@ -331,10 +341,10 @@ } /** - * @return mixed null + * @return mixed LoggerThrowableInformation */ public function getThrowableInformation() { - return null; + return $this->throwableInfo; } /** Index: src/main/php/LoggerThrowableInformation.php =================================================================== --- src/main/php/LoggerThrowableInformation.php (revision 0) +++ src/main/php/LoggerThrowableInformation.php (revision 0) @@ -0,0 +1,129 @@ + + * @package log4php + * @version 0.9a + */ +class LoggerThrowableInformation { + + /** + * @var Exception Throwable to log + */ + protected $throwable; + /** + * @var array Array of throwable messages + */ + protected $rep; + /** + * @var Logger reference + */ + protected $logger; + + /** + * @desc Create a new instance + */ + public function __construct() { + if (func_num_args() == 1 && is_array(func_get_arg(0))) { + $rep = func_get_arg(0); + $this->__construct1($rep); + } else if (func_num_args() == 1 && func_get_arg(0) instanceof Exception) { + $ex = func_get_arg(0); + $this->__construct2($ex); + } else if (func_num_args() == 2 && func_get_arg(0) instanceof Exception && (func_get_arg(1) instanceof Logger || func_get_arg(1) === null)) { + $ex = func_get_arg(0); + $logger = func_get_arg(1); + $this->__construct3($ex, $logger); + } else { + throw new InvalidArgumentException(); + } + } + + /** + * @desc Create a new instance from String representation of throwable + * + * @param array String representation of throwable + */ + public function __construct1(array $rep) { + $this->rep = $rep; + } + + /** + * @desc Create a new instance from throwable + * + * @param Exception $ex throwable + */ + public function __construct2(Exception $ex) { + $this->throwable = $ex; + } + + /** + * @desc Create a new instance from throwable and Logger + * + * @param Exception $ex throwable, may not be null + * @param Logger $logger Logger used to obtain throwable renderer, may be null + */ + public function __construct3(Exception $ex, Logger $logger = null) { + $this->throwable = $ex; + $this->logger = $logger; + } + + /** + * @return Exception + */ + public function getThrowable() { + return $this->throwable; + } + + /** + * @desc Returns string representation of throwable + * + * @return array + */ + public function getThrowableStrRep() { + if (!is_array($this->rep) && $this->throwable !== null) { + $this->rep = array(); + $ex = $this->throwable; + $this->rep[] = $ex->getMessage(); + while (method_exists($ex, 'getPrevious')) { + $ex = $ex->getPrevious(); + if ($ex !== null && $ex instanceof Exception) { + $this->rep[] = $ex->getMessage(); + } + } + } + + return $this->rep; + } +} +?> \ No newline at end of file Index: src/main/php/renderers/LoggerRendererException.php =================================================================== --- src/main/php/renderers/LoggerRendererException.php (revision 0) +++ src/main/php/renderers/LoggerRendererException.php (revision 0) @@ -0,0 +1,58 @@ + + * @package log4php + * @subpackage renderers + * @version 0.9a + */ +class LoggerRendererException implements LoggerRendererObject { + + public function render($o) { + $ex = $o; + $fullTrace = $this->getExceptionAsString($ex); + while (method_exists($ex, 'getPrevious')) { + $ex = $ex->getPrevious(); + if ($ex !== null && $ex instanceof Exception) { + $fullTrace .= sprintf('%s%s: %s', PHP_EOL, 'Caused by', $this->getExceptionAsString($ex)); + } + } + + return $fullTrace; + } + + protected function getExceptionAsString(Exception $ex) { + return sprintf('%s: %s%s%s' ,get_class($ex), $ex->getMessage(), PHP_EOL, $ex->getTraceAsString()); + } +} +?> \ No newline at end of file Index: src/test/php/LoggerLoggingEventTest.php =================================================================== --- src/test/php/LoggerLoggingEventTest.php (revision 930537) +++ src/test/php/LoggerLoggingEventTest.php (working copy) @@ -40,13 +40,15 @@ } public function format(LoggerLoggingEvent $event) { - LoggerLoggingEventTest::$locationInfo = $event->getLocationInformation(); + LoggerLoggingEventTest::$locationInfo = $event->getLocationInformation(); + LoggerLoggingEventTest::$throwableInfo = $event->getThrowableInformation(); } } class LoggerLoggingEventTest extends PHPUnit_Framework_TestCase { public static $locationInfo; + public static $throwableInfo; public function testConstructWithLoggerName() { $l = LoggerLevel :: getLevelDebug(); @@ -89,5 +91,46 @@ self::assertEquals($li->getMethodName(), __FUNCTION__); } - + + public function testGetThrowableInformation1() { + $hierarchy = Logger::getHierarchy(); + $root = $hierarchy->getRootLogger(); + + $a = new LoggerLoggingEventTestCaseAppender('A1'); + $a->setLayout( new LoggerLoggingEventTestCaseLayout() ); + $root->addAppender($a); + + $logger = $hierarchy->getLogger('test'); + $logger->debug('test'); + $hierarchy->shutdown(); + + $ti = self::$throwableInfo; + + self::assertEquals($ti, null); + } + + public function testGetThrowableInformation2() { + $hierarchy = Logger::getHierarchy(); + $root = $hierarchy->getRootLogger(); + + $a = new LoggerLoggingEventTestCaseAppender('A1'); + $a->setLayout( new LoggerLoggingEventTestCaseLayout() ); + $root->addAppender($a); + + $ex = new Exception('Message1'); + $logger = $hierarchy->getLogger('test'); + $logger->debug('test', $ex); + $hierarchy->shutdown(); + + $ti = self::$throwableInfo; + + self::assertTrue($ti instanceof LoggerThrowableInformation); + $expected = $ex; + $result = $ti->getThrowable(); + self::assertEquals($expected, $result); + + $expected = array('Message1'); + $result = $ti->getThrowableStrRep(); + self::assertEquals($expected, $result); + } } Index: src/test/php/LoggerThrowableInformationTest.php =================================================================== --- src/test/php/LoggerThrowableInformationTest.php (revision 0) +++ src/test/php/LoggerThrowableInformationTest.php (revision 0) @@ -0,0 +1,126 @@ +getThrowableStrRep(); + $this->assertEquals($expected, $result); + + $expected = null; + $result = $tInfo->getThrowable(); + $this->assertEquals($expected, $result); + } + + public function testConstructor2() { + $ex = new LoggerThrowableInformationTestException('Message1'); + $tInfo = new LoggerThrowableInformation($ex); + + $expected = $ex; + $result = $tInfo->getThrowable(); + $this->assertEquals($expected, $result); + + $expected = array('Message1'); + $result = $tInfo->getThrowableStrRep(); + $this->assertEquals($expected, $result); + } + + public function testConstructor3() { + $ex = new LoggerThrowableInformationTestException('Message1'); + $logger = Logger::getLogger('test'); + $tInfo = new LoggerThrowableInformation($ex, $logger); + + $expected = $ex; + $result = $tInfo->getThrowable(); + $this->assertEquals($expected, $result); + + $expected = array('Message1'); + $result = $tInfo->getThrowableStrRep(); + $this->assertEquals($expected, $result); + } + + public function testInvalidConstructor() { + try { + $tInfo = new LoggerThrowableInformation('test'); + } catch (InvalidArgumentException $ex) { + return; + } + + $this->fail('Invalid constructor params should raise Exception'); + } + + public function testExceptionChain() { + $ex1 = new LoggerThrowableInformationTestException('Message1'); + $ex2 = new LoggerThrowableInformationTestException('Message2', 0, $ex1); + $ex3 = new LoggerThrowableInformationTestException('Message3', 0, $ex2); + + $tInfo = new LoggerThrowableInformation($ex3); + $expected = array( + 'Message3', + 'Message2', + 'Message1' + ); + $result = $tInfo->getThrowableStrRep(); + $this->assertEquals($expected, $result); + } +} + + +if (version_compare(PHP_VERSION, '5.3.0') >= 0) { + class LoggerThrowableInformationTestException extends Exception { } +} else { + class LoggerThrowableInformationTestException extends Exception { + + protected $previous; + + public function __construct($message = '', $code = 0, Exception $previous = null) { + parent::__construct($message, $code); + $this->previous = $previous; + } + + public function getPrevious() { + return $this->previous; + } + } +} +?> \ No newline at end of file Index: src/test/php/renderers/LoggerRendererExceptionTest.php =================================================================== --- src/test/php/renderers/LoggerRendererExceptionTest.php (revision 0) +++ src/test/php/renderers/LoggerRendererExceptionTest.php (revision 0) @@ -0,0 +1,82 @@ +render($ex3); + + $expected = 3; + $result = substr_count($rendered, 'LoggerRendererExceptionTestException: Message'); + $this->assertEquals($expected, $result); + + $expected = 2; + $result = substr_count($rendered, 'Caused by: LoggerRendererExceptionTestException:'); + $this->assertEquals($expected, $result); + + $expected = 1; + $result = substr_count($rendered, 'Caused by: LoggerRendererExceptionTestException: Message2'); + $this->assertEquals($expected, $result); + + $expected = 1; + $result = substr_count($rendered, 'Caused by: LoggerRendererExceptionTestException: Message1'); + $this->assertEquals($expected, $result); + } +} + +if (version_compare(PHP_VERSION, '5.3.0') >= 0) { + class LoggerRendererExceptionTestException extends Exception { } +} else { + class LoggerRendererExceptionTestException extends Exception { + + protected $previous; + + public function __construct($message = '', $code = 0, Exception $previous = null) { + parent::__construct($message, $code); + $this->previous = $previous; + } + + public function getPrevious() { + return $this->previous; + } + } +} +?> \ No newline at end of file