diff --git a/.travis.yml b/.travis.yml index b821b452..07bad617 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ php: - 5.6 - 7.0 - 7.1 - - hhvm + - 7.2 - nightly matrix: diff --git a/README.md b/README.md index 7d8ade52..d7569446 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) [![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) -[![Reference Status](https://www.versioneye.com/php/monolog:monolog/reference_badge.svg)](https://www.versioneye.com/php/monolog:monolog/references) Monolog sends your logs to files, sockets, inboxes, databases and various diff --git a/doc/02-handlers-formatters-processors.md b/doc/02-handlers-formatters-processors.md index bea968ac..af45913a 100644 --- a/doc/02-handlers-formatters-processors.md +++ b/doc/02-handlers-formatters-processors.md @@ -55,6 +55,7 @@ - _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account. - _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server. - _LogEntriesHandler_: Logs records to a [LogEntries](http://logentries.com/) account. +- _InsightOpsHandler_: Logs records to a [InsightOps](https://www.rapid7.com/products/insightops/) account. ### Logging in development diff --git a/src/Monolog/ErrorHandler.php b/src/Monolog/ErrorHandler.php index f0ff3d8c..b96c58ae 100644 --- a/src/Monolog/ErrorHandler.php +++ b/src/Monolog/ErrorHandler.php @@ -39,6 +39,7 @@ class ErrorHandler private $hasFatalErrorHandler; private $fatalLevel; private $reservedMemory; + private $lastFatalTrace; private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); private $previousSignalHandler = array(); @@ -192,6 +193,13 @@ class ErrorHandler if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) { $level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL; $this->logger->log($level, self::codeToString($code).': '.$message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line)); + } else { + // http://php.net/manual/en/function.debug-backtrace.php + // As of 5.3.6, DEBUG_BACKTRACE_IGNORE_ARGS option was added. + // Any version less than 5.3.6 must use the DEBUG_BACKTRACE_IGNORE_ARGS constant value '2'. + $trace = debug_backtrace((PHP_VERSION_ID < 50306) ? 2 : DEBUG_BACKTRACE_IGNORE_ARGS); + array_shift($trace); // Exclude handleError from trace + $this->lastFatalTrace = $trace; } if ($this->previousErrorHandler === true) { @@ -213,7 +221,7 @@ class ErrorHandler $this->logger->log( $this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel, 'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], - array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line']) + array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $this->lastFatalTrace) ); if ($this->logger instanceof Logger) { diff --git a/src/Monolog/Formatter/FluentdFormatter.php b/src/Monolog/Formatter/FluentdFormatter.php index 02632bb5..46a91ffe 100644 --- a/src/Monolog/Formatter/FluentdFormatter.php +++ b/src/Monolog/Formatter/FluentdFormatter.php @@ -62,6 +62,7 @@ class FluentdFormatter implements FormatterInterface $message = array( 'message' => $record['message'], + 'context' => $record['context'], 'extra' => $record['extra'], ); diff --git a/src/Monolog/Formatter/HtmlFormatter.php b/src/Monolog/Formatter/HtmlFormatter.php index 3eec95f6..dfc0b4a3 100644 --- a/src/Monolog/Formatter/HtmlFormatter.php +++ b/src/Monolog/Formatter/HtmlFormatter.php @@ -58,7 +58,7 @@ class HtmlFormatter extends NormalizerFormatter $td = '
'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'
'; } - return "\n$th:\n".$td."\n"; + return "\n$th:\n".$td."\n"; } /** diff --git a/src/Monolog/Formatter/JsonFormatter.php b/src/Monolog/Formatter/JsonFormatter.php index 0782f149..b8309b10 100644 --- a/src/Monolog/Formatter/JsonFormatter.php +++ b/src/Monolog/Formatter/JsonFormatter.php @@ -138,18 +138,23 @@ class JsonFormatter extends NormalizerFormatter * * @return mixed */ - protected function normalize($data) + protected function normalize($data, $depth = 0) { + if ($depth > 9) { + return 'Over 9 levels deep, aborting normalization'; + } + if (is_array($data) || $data instanceof \Traversable) { $normalized = array(); $count = 1; foreach ($data as $key => $value) { - if ($count++ >= 1000) { - $normalized['...'] = 'Over 1000 items, aborting normalization'; + if ($count++ > 1000) { + $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization'; break; } - $normalized[$key] = $this->normalize($value); + + $normalized[$key] = $this->normalize($value, $depth+1); } return $normalized; diff --git a/src/Monolog/Formatter/NormalizerFormatter.php b/src/Monolog/Formatter/NormalizerFormatter.php index d4414882..91227241 100644 --- a/src/Monolog/Formatter/NormalizerFormatter.php +++ b/src/Monolog/Formatter/NormalizerFormatter.php @@ -55,8 +55,12 @@ class NormalizerFormatter implements FormatterInterface return $records; } - protected function normalize($data) + protected function normalize($data, $depth = 0) { + if ($depth > 9) { + return 'Over 9 levels deep, aborting normalization'; + } + if (null === $data || is_scalar($data)) { if (is_float($data)) { if (is_infinite($data)) { @@ -75,11 +79,12 @@ class NormalizerFormatter implements FormatterInterface $count = 1; foreach ($data as $key => $value) { - if ($count++ >= 1000) { + if ($count++ > 1000) { $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization'; break; } - $normalized[$key] = $this->normalize($value); + + $normalized[$key] = $this->normalize($value, $depth+1); } return $normalized; diff --git a/src/Monolog/Formatter/WildfireFormatter.php b/src/Monolog/Formatter/WildfireFormatter.php index 654710a8..65dba99c 100644 --- a/src/Monolog/Formatter/WildfireFormatter.php +++ b/src/Monolog/Formatter/WildfireFormatter.php @@ -102,12 +102,12 @@ class WildfireFormatter extends NormalizerFormatter throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); } - protected function normalize($data) + protected function normalize($data, $depth = 0) { if (is_object($data) && !$data instanceof \DateTime) { return $data; } - return parent::normalize($data); + return parent::normalize($data, $depth); } } diff --git a/src/Monolog/Handler/AbstractHandler.php b/src/Monolog/Handler/AbstractHandler.php index 758a425c..bf56549d 100644 --- a/src/Monolog/Handler/AbstractHandler.php +++ b/src/Monolog/Handler/AbstractHandler.php @@ -32,8 +32,8 @@ abstract class AbstractHandler implements HandlerInterface protected $processors = array(); /** - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($level = Logger::DEBUG, $bubble = true) { @@ -141,8 +141,8 @@ abstract class AbstractHandler implements HandlerInterface /** * Sets the bubbling behavior. * - * @param Boolean $bubble true means that this handler allows bubbling. - * false means that bubbling is not permitted. + * @param bool $bubble true means that this handler allows bubbling. + * false means that bubbling is not permitted. * @return self */ public function setBubble($bubble) @@ -155,8 +155,8 @@ abstract class AbstractHandler implements HandlerInterface /** * Gets the bubbling behavior. * - * @return Boolean true means that this handler allows bubbling. - * false means that bubbling is not permitted. + * @return bool true means that this handler allows bubbling. + * false means that bubbling is not permitted. */ public function getBubble() { diff --git a/src/Monolog/Handler/AbstractSyslogHandler.php b/src/Monolog/Handler/AbstractSyslogHandler.php index e2b2832d..8c76aca0 100644 --- a/src/Monolog/Handler/AbstractSyslogHandler.php +++ b/src/Monolog/Handler/AbstractSyslogHandler.php @@ -53,9 +53,9 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler ); /** - * @param mixed $facility - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param mixed $facility + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) { diff --git a/src/Monolog/Handler/BrowserConsoleHandler.php b/src/Monolog/Handler/BrowserConsoleHandler.php index b3a21bd4..0225ee71 100644 --- a/src/Monolog/Handler/BrowserConsoleHandler.php +++ b/src/Monolog/Handler/BrowserConsoleHandler.php @@ -43,11 +43,11 @@ class BrowserConsoleHandler extends AbstractProcessingHandler protected function write(array $record) { // Accumulate records - self::$records[] = $record; + static::$records[] = $record; // Register shutdown handler if not already done - if (!self::$initialized) { - self::$initialized = true; + if (!static::$initialized) { + static::$initialized = true; $this->registerShutdownFunction(); } } @@ -58,18 +58,18 @@ class BrowserConsoleHandler extends AbstractProcessingHandler */ public static function send() { - $format = self::getResponseFormat(); + $format = static::getResponseFormat(); if ($format === 'unknown') { return; } - if (count(self::$records)) { + if (count(static::$records)) { if ($format === 'html') { - self::writeOutput(''); + static::writeOutput(''); } elseif ($format === 'js') { - self::writeOutput(self::generateScript()); + static::writeOutput(static::generateScript()); } - self::reset(); + static::reset(); } } @@ -78,7 +78,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler */ public static function reset() { - self::$records = array(); + static::$records = array(); } /** @@ -133,18 +133,18 @@ class BrowserConsoleHandler extends AbstractProcessingHandler private static function generateScript() { $script = array(); - foreach (self::$records as $record) { - $context = self::dump('Context', $record['context']); - $extra = self::dump('Extra', $record['extra']); + foreach (static::$records as $record) { + $context = static::dump('Context', $record['context']); + $extra = static::dump('Extra', $record['extra']); if (empty($context) && empty($extra)) { - $script[] = self::call_array('log', self::handleStyles($record['formatted'])); + $script[] = static::call_array('log', static::handleStyles($record['formatted'])); } else { $script = array_merge($script, - array(self::call_array('groupCollapsed', self::handleStyles($record['formatted']))), + array(static::call_array('groupCollapsed', static::handleStyles($record['formatted']))), $context, $extra, - array(self::call('groupEnd')) + array(static::call('groupEnd')) ); } } @@ -154,19 +154,19 @@ class BrowserConsoleHandler extends AbstractProcessingHandler private static function handleStyles($formatted) { - $args = array(self::quote('font-weight: normal')); + $args = array(static::quote('font-weight: normal')); $format = '%c' . $formatted; preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); foreach (array_reverse($matches) as $match) { - $args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0])); + $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0])); $args[] = '"font-weight: normal"'; $pos = $match[0][1]; $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0])); } - array_unshift($args, self::quote($format)); + array_unshift($args, static::quote($format)); return $args; } @@ -198,13 +198,13 @@ class BrowserConsoleHandler extends AbstractProcessingHandler if (empty($dict)) { return $script; } - $script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title)); + $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title)); foreach ($dict as $key => $value) { $value = json_encode($value); if (empty($value)) { - $value = self::quote(''); + $value = static::quote(''); } - $script[] = self::call('log', self::quote('%s: %o'), self::quote($key), $value); + $script[] = static::call('log', static::quote('%s: %o'), static::quote($key), $value); } return $script; @@ -220,7 +220,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler $args = func_get_args(); $method = array_shift($args); - return self::call_array($method, $args); + return static::call_array($method, $args); } private static function call_array($method, array $args) diff --git a/src/Monolog/Handler/BufferHandler.php b/src/Monolog/Handler/BufferHandler.php index 72f89535..c15e28a2 100644 --- a/src/Monolog/Handler/BufferHandler.php +++ b/src/Monolog/Handler/BufferHandler.php @@ -34,8 +34,8 @@ class BufferHandler extends AbstractHandler * @param HandlerInterface $handler Handler. * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param Boolean $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded */ public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false) { diff --git a/src/Monolog/Handler/ChromePHPHandler.php b/src/Monolog/Handler/ChromePHPHandler.php index 785cb0c9..37419a06 100644 --- a/src/Monolog/Handler/ChromePHPHandler.php +++ b/src/Monolog/Handler/ChromePHPHandler.php @@ -32,7 +32,7 @@ class ChromePHPHandler extends AbstractProcessingHandler * Header name */ const HEADER_NAME = 'X-ChromeLogger-Data'; - + /** * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) */ @@ -45,7 +45,7 @@ class ChromePHPHandler extends AbstractProcessingHandler * * Chrome limits the headers to 256KB, so when we sent 240KB we stop sending * - * @var Boolean + * @var bool */ protected static $overflowed = false; @@ -58,8 +58,8 @@ class ChromePHPHandler extends AbstractProcessingHandler protected static $sendHeaders = true; /** - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($level = Logger::DEBUG, $bubble = true) { @@ -174,7 +174,7 @@ class ChromePHPHandler extends AbstractProcessingHandler /** * Verifies if the headers are accepted by the current user agent * - * @return Boolean + * @return bool */ protected function headersAccepted() { diff --git a/src/Monolog/Handler/DeduplicationHandler.php b/src/Monolog/Handler/DeduplicationHandler.php index 7778c22a..35b55cb4 100644 --- a/src/Monolog/Handler/DeduplicationHandler.php +++ b/src/Monolog/Handler/DeduplicationHandler.php @@ -60,7 +60,7 @@ class DeduplicationHandler extends BufferHandler * @param string $deduplicationStore The file/path where the deduplication log should be kept * @param int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = true) { diff --git a/src/Monolog/Handler/ElasticSearchHandler.php b/src/Monolog/Handler/ElasticSearchHandler.php index 81967406..bb0f83eb 100644 --- a/src/Monolog/Handler/ElasticSearchHandler.php +++ b/src/Monolog/Handler/ElasticSearchHandler.php @@ -46,10 +46,10 @@ class ElasticSearchHandler extends AbstractProcessingHandler protected $options = array(); /** - * @param Client $client Elastica Client object - * @param array $options Handler configuration - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Client $client Elastica Client object + * @param array $options Handler configuration + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true) { diff --git a/src/Monolog/Handler/ErrorLogHandler.php b/src/Monolog/Handler/ErrorLogHandler.php index 1447a584..b2986b0f 100644 --- a/src/Monolog/Handler/ErrorLogHandler.php +++ b/src/Monolog/Handler/ErrorLogHandler.php @@ -28,10 +28,10 @@ class ErrorLogHandler extends AbstractProcessingHandler protected $expandNewlines; /** - * @param int $messageType Says where the error should go. - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param Boolean $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries + * @param int $messageType Says where the error should go. + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries */ public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = true, $expandNewlines = false) { diff --git a/src/Monolog/Handler/FilterHandler.php b/src/Monolog/Handler/FilterHandler.php index 2a0f7fd1..938c1a7e 100644 --- a/src/Monolog/Handler/FilterHandler.php +++ b/src/Monolog/Handler/FilterHandler.php @@ -40,7 +40,7 @@ class FilterHandler extends AbstractHandler /** * Whether the messages that are handled can bubble up the stack or not * - * @var Boolean + * @var bool */ protected $bubble; @@ -48,7 +48,7 @@ class FilterHandler extends AbstractHandler * @param callable|HandlerInterface $handler Handler or factory callable($record, $this). * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = true) { diff --git a/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php index c3e42efe..aaca12cc 100644 --- a/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php +++ b/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php @@ -22,7 +22,7 @@ interface ActivationStrategyInterface * Returns whether the given record activates the handler. * * @param array $record - * @return Boolean + * @return bool */ public function isHandlerActivated(array $record); } diff --git a/src/Monolog/Handler/FingersCrossedHandler.php b/src/Monolog/Handler/FingersCrossedHandler.php index d1dcaacf..d8026be6 100644 --- a/src/Monolog/Handler/FingersCrossedHandler.php +++ b/src/Monolog/Handler/FingersCrossedHandler.php @@ -41,8 +41,8 @@ class FingersCrossedHandler extends AbstractHandler * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) * @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered */ public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null) diff --git a/src/Monolog/Handler/FirePHPHandler.php b/src/Monolog/Handler/FirePHPHandler.php index fee47950..c30b1843 100644 --- a/src/Monolog/Handler/FirePHPHandler.php +++ b/src/Monolog/Handler/FirePHPHandler.php @@ -158,7 +158,7 @@ class FirePHPHandler extends AbstractProcessingHandler /** * Verifies if the headers are accepted by the current user agent * - * @return Boolean + * @return bool */ protected function headersAccepted() { diff --git a/src/Monolog/Handler/GelfHandler.php b/src/Monolog/Handler/GelfHandler.php index d3847d82..71e46693 100644 --- a/src/Monolog/Handler/GelfHandler.php +++ b/src/Monolog/Handler/GelfHandler.php @@ -47,14 +47,6 @@ class GelfHandler extends AbstractProcessingHandler $this->publisher = $publisher; } - /** - * {@inheritdoc} - */ - public function close() - { - $this->publisher = null; - } - /** * {@inheritdoc} */ diff --git a/src/Monolog/Handler/GroupHandler.php b/src/Monolog/Handler/GroupHandler.php index 663f5a92..c38508c2 100644 --- a/src/Monolog/Handler/GroupHandler.php +++ b/src/Monolog/Handler/GroupHandler.php @@ -23,8 +23,8 @@ class GroupHandler extends AbstractHandler protected $handlers; /** - * @param array $handlers Array of Handlers. - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param array $handlers Array of Handlers. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(array $handlers, $bubble = true) { diff --git a/src/Monolog/Handler/HandlerInterface.php b/src/Monolog/Handler/HandlerInterface.php index d920c4ba..8d5a4a09 100644 --- a/src/Monolog/Handler/HandlerInterface.php +++ b/src/Monolog/Handler/HandlerInterface.php @@ -31,7 +31,7 @@ interface HandlerInterface * * @param array $record Partial log record containing only a level key * - * @return Boolean + * @return bool */ public function isHandling(array $record); @@ -46,7 +46,7 @@ interface HandlerInterface * calling further handlers in the stack with a given log record. * * @param array $record The record to handle - * @return Boolean true means that this handler handled the record, and that bubbling is not permitted. + * @return bool true means that this handler handled the record, and that bubbling is not permitted. * false means the record was either not processed or that this handler allows bubbling. */ public function handle(array $record); diff --git a/src/Monolog/Handler/IFTTTHandler.php b/src/Monolog/Handler/IFTTTHandler.php index d60a3c82..7f226220 100644 --- a/src/Monolog/Handler/IFTTTHandler.php +++ b/src/Monolog/Handler/IFTTTHandler.php @@ -30,10 +30,10 @@ class IFTTTHandler extends AbstractProcessingHandler private $secretKey; /** - * @param string $eventName The name of the IFTTT Maker event that should be triggered - * @param string $secretKey A valid IFTTT secret key - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $eventName The name of the IFTTT Maker event that should be triggered + * @param string $secretKey A valid IFTTT secret key + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($eventName, $secretKey, $level = Logger::ERROR, $bubble = true) { diff --git a/src/Monolog/Handler/InsightOpsHandler.php b/src/Monolog/Handler/InsightOpsHandler.php new file mode 100644 index 00000000..a12e3de5 --- /dev/null +++ b/src/Monolog/Handler/InsightOpsHandler.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + namespace Monolog\Handler; + + use Monolog\Logger; + +/** + * Inspired on LogEntriesHandler. + * + * @author Robert Kaufmann III + * @author Gabriel Machado + */ +class InsightOpsHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by InsightOps + * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'. + * @param bool $useSSL Whether or not SSL encryption should be used + * @param int $level The minimum logging level to trigger this handler + * @param bool $bubble Whether or not messages that are handled should bubble up the stack. + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct($token, $region = 'us', $useSSL = true, $level = Logger::DEBUG, $bubble = true) + { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); + } + + $endpoint = $useSSL + ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443' + : $region . '.data.logs.insight.rapid7.com:80'; + + parent::__construct($endpoint, $level, $bubble); + $this->logToken = $token; + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/src/Monolog/Handler/MandrillHandler.php b/src/Monolog/Handler/MandrillHandler.php index ab95924f..3f0956a9 100644 --- a/src/Monolog/Handler/MandrillHandler.php +++ b/src/Monolog/Handler/MandrillHandler.php @@ -27,7 +27,7 @@ class MandrillHandler extends MailHandler * @param string $apiKey A valid Mandrill API key * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($apiKey, $message, $level = Logger::ERROR, $bubble = true) { diff --git a/src/Monolog/Handler/NewRelicHandler.php b/src/Monolog/Handler/NewRelicHandler.php index 6718e9e0..ed2efa27 100644 --- a/src/Monolog/Handler/NewRelicHandler.php +++ b/src/Monolog/Handler/NewRelicHandler.php @@ -84,7 +84,7 @@ class NewRelicHandler extends AbstractProcessingHandler unset($record['formatted']['context']['transaction_name']); } - if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Exception) { + if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) { newrelic_notice_error($record['message'], $record['context']['exception']); unset($record['formatted']['context']['exception']); } else { diff --git a/src/Monolog/Handler/PsrHandler.php b/src/Monolog/Handler/PsrHandler.php index 1ae85845..a99e6ab7 100644 --- a/src/Monolog/Handler/PsrHandler.php +++ b/src/Monolog/Handler/PsrHandler.php @@ -31,7 +31,7 @@ class PsrHandler extends AbstractHandler /** * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, $bubble = true) { diff --git a/src/Monolog/Handler/PushoverHandler.php b/src/Monolog/Handler/PushoverHandler.php index bba72005..f27bb3da 100644 --- a/src/Monolog/Handler/PushoverHandler.php +++ b/src/Monolog/Handler/PushoverHandler.php @@ -69,8 +69,8 @@ class PushoverHandler extends SocketHandler * @param string|array $users Pushover user id or array of ids the message will be sent to * @param string $title Title sent to the Pushover API * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not * the pushover.net app owner. OpenSSL is required for this option. * @param int $highPriorityLevel The minimum logging level at which this handler will start * sending "high priority" requests to the Pushover API @@ -180,6 +180,6 @@ class PushoverHandler extends SocketHandler */ public function useFormattedMessage($value) { - $this->useFormattedMessage = (boolean) $value; + $this->useFormattedMessage = (bool) $value; } } diff --git a/src/Monolog/Handler/RavenHandler.php b/src/Monolog/Handler/RavenHandler.php index 53a8b391..34ff0091 100644 --- a/src/Monolog/Handler/RavenHandler.php +++ b/src/Monolog/Handler/RavenHandler.php @@ -57,7 +57,7 @@ class RavenHandler extends AbstractProcessingHandler /** * @param Raven_Client $ravenClient * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true) { @@ -180,7 +180,7 @@ class RavenHandler extends AbstractProcessingHandler } if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $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'], array(), $options); diff --git a/src/Monolog/Handler/RotatingFileHandler.php b/src/Monolog/Handler/RotatingFileHandler.php index 3b60b3d1..cc9fd4d3 100644 --- a/src/Monolog/Handler/RotatingFileHandler.php +++ b/src/Monolog/Handler/RotatingFileHandler.php @@ -39,9 +39,9 @@ class RotatingFileHandler extends StreamHandler * @param string $filename * @param int $maxFiles The maximal amount of files to keep (0 means unlimited) * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) - * @param Boolean $useLocking Try to lock log file before doing any writes + * @param bool $useLocking Try to lock log file before doing any writes */ public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) { @@ -166,7 +166,7 @@ class RotatingFileHandler extends StreamHandler $fileInfo = pathinfo($this->filename); $glob = str_replace( array('{filename}', '{date}'), - array($fileInfo['filename'], '*'), + array($fileInfo['filename'], '[0-9][0-9][0-9][0-9]*'), $fileInfo['dirname'] . '/' . $this->filenameFormat ); if (!empty($fileInfo['extension'])) { diff --git a/src/Monolog/Handler/Slack/SlackRecord.php b/src/Monolog/Handler/Slack/SlackRecord.php old mode 100644 new mode 100755 index 38bc838a..e55e0e2e --- a/src/Monolog/Handler/Slack/SlackRecord.php +++ b/src/Monolog/Handler/Slack/SlackRecord.php @@ -146,7 +146,7 @@ class SlackRecord if ($this->useShortAttachment) { $attachment['fields'][] = $this->generateAttachmentField( - ucfirst($key), + $key, $record[$key] ); } else { @@ -229,8 +229,8 @@ class SlackRecord /** * Generates attachment field * - * @param string $title - * @param string|array $value\ + * @param string $title + * @param string|array $value * * @return array */ @@ -241,7 +241,7 @@ class SlackRecord : $value; return array( - 'title' => $title, + 'title' => ucfirst($title), 'value' => $value, 'short' => false ); @@ -257,7 +257,7 @@ class SlackRecord private function generateAttachmentFields(array $data) { $fields = array(); - foreach ($data as $key => $value) { + foreach ($this->normalizerFormatter->format($data) as $key => $value) { $fields[] = $this->generateAttachmentField($key, $value); } diff --git a/src/Monolog/Handler/SlackHandler.php b/src/Monolog/Handler/SlackHandler.php index 3ac4d836..45d634f4 100644 --- a/src/Monolog/Handler/SlackHandler.php +++ b/src/Monolog/Handler/SlackHandler.php @@ -75,6 +75,11 @@ class SlackHandler extends SocketHandler return $this->slackRecord; } + public function getToken() + { + return $this->token; + } + /** * {@inheritdoc} * diff --git a/src/Monolog/Handler/SlackWebhookHandler.php b/src/Monolog/Handler/SlackWebhookHandler.php index 9a1bbb44..1ef85fae 100644 --- a/src/Monolog/Handler/SlackWebhookHandler.php +++ b/src/Monolog/Handler/SlackWebhookHandler.php @@ -70,6 +70,11 @@ class SlackWebhookHandler extends AbstractProcessingHandler return $this->slackRecord; } + public function getWebhookUrl() + { + return $this->webhookUrl; + } + /** * {@inheritdoc} * diff --git a/src/Monolog/Handler/SocketHandler.php b/src/Monolog/Handler/SocketHandler.php index 7a61bf4e..db50d97f 100644 --- a/src/Monolog/Handler/SocketHandler.php +++ b/src/Monolog/Handler/SocketHandler.php @@ -27,15 +27,16 @@ class SocketHandler extends AbstractProcessingHandler private $timeout = 0; private $writingTimeout = 10; private $lastSentBytes = null; + private $chunkSize = null; private $persistent = false; private $errno; private $errstr; private $lastWritingAt; /** - * @param string $connectionString Socket connection string - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $connectionString Socket connection string + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) { @@ -87,7 +88,7 @@ class SocketHandler extends AbstractProcessingHandler */ public function setPersistent($persistent) { - $this->persistent = (boolean) $persistent; + $this->persistent = (bool) $persistent; } /** @@ -127,6 +128,16 @@ class SocketHandler extends AbstractProcessingHandler $this->writingTimeout = (float) $seconds; } + /** + * Set chunk size. Only has effect during connection in the writing cycle. + * + * @param float $bytes + */ + public function setChunkSize($bytes) + { + $this->chunkSize = $bytes; + } + /** * Get current connection string * @@ -177,6 +188,16 @@ class SocketHandler extends AbstractProcessingHandler return $this->writingTimeout; } + /** + * Get current chunk size + * + * @return float + */ + public function getChunkSize() + { + return $this->chunkSize; + } + /** * Check to see if the socket is currently available. * @@ -219,6 +240,16 @@ class SocketHandler extends AbstractProcessingHandler return stream_set_timeout($this->resource, $seconds, $microseconds); } + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-chunk-size.php + */ + protected function streamSetChunkSize() + { + return stream_set_chunk_size($this->resource, $this->chunkSize); + } + /** * Wrapper to allow mocking */ @@ -268,6 +299,7 @@ class SocketHandler extends AbstractProcessingHandler { $this->createSocketResource(); $this->setSocketTimeout(); + $this->setStreamChunkSize(); } private function createSocketResource() @@ -290,6 +322,13 @@ class SocketHandler extends AbstractProcessingHandler } } + private function setStreamChunkSize() + { + if ($this->chunkSize && !$this->streamSetChunkSize()) { + throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()"); + } + } + private function writeToSocket($data) { $length = strlen($data); diff --git a/src/Monolog/Handler/StreamHandler.php b/src/Monolog/Handler/StreamHandler.php index 09a15738..a35b7e4c 100644 --- a/src/Monolog/Handler/StreamHandler.php +++ b/src/Monolog/Handler/StreamHandler.php @@ -32,9 +32,9 @@ class StreamHandler extends AbstractProcessingHandler /** * @param resource|string $stream * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) - * @param Boolean $useLocking Try to lock log file before doing any writes + * @param bool $useLocking Try to lock log file before doing any writes * * @throws \Exception If a missing directory is not buildable * @throws \InvalidArgumentException If stream is not a resource or string @@ -167,7 +167,7 @@ class StreamHandler extends AbstractProcessingHandler set_error_handler(array($this, 'customErrorHandler')); $status = mkdir($dir, 0777, true); restore_error_handler(); - if (false === $status) { + if (false === $status && !is_dir($dir)) { throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir)); } } diff --git a/src/Monolog/Handler/SwiftMailerHandler.php b/src/Monolog/Handler/SwiftMailerHandler.php index 72f44a53..ac7b16ff 100644 --- a/src/Monolog/Handler/SwiftMailerHandler.php +++ b/src/Monolog/Handler/SwiftMailerHandler.php @@ -12,6 +12,7 @@ namespace Monolog\Handler; use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\LineFormatter; use Swift; @@ -29,7 +30,7 @@ class SwiftMailerHandler extends MailHandler * @param \Swift_Mailer $mailer The mailer to use * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true) { @@ -47,6 +48,17 @@ class SwiftMailerHandler extends MailHandler $this->mailer->send($this->buildMessage($content, $records)); } + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string $format The format of the subject + * @return FormatterInterface + */ + protected function getSubjectFormatter($format) + { + return new LineFormatter($format); + } + /** * Creates instance of Swift_Message to be sent * @@ -69,7 +81,7 @@ class SwiftMailerHandler extends MailHandler } if ($records) { - $subjectFormatter = new LineFormatter($message->getSubject()); + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); $message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); } diff --git a/src/Monolog/Handler/SyslogHandler.php b/src/Monolog/Handler/SyslogHandler.php index 376bc3b2..f770c802 100644 --- a/src/Monolog/Handler/SyslogHandler.php +++ b/src/Monolog/Handler/SyslogHandler.php @@ -32,11 +32,11 @@ class SyslogHandler extends AbstractSyslogHandler protected $logopts; /** - * @param string $ident - * @param mixed $facility - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID + * @param string $ident + * @param mixed $facility + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID */ public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $logopts = LOG_PID) { diff --git a/src/Monolog/Handler/SyslogUdpHandler.php b/src/Monolog/Handler/SyslogUdpHandler.php index 4718711b..e14b378c 100644 --- a/src/Monolog/Handler/SyslogUdpHandler.php +++ b/src/Monolog/Handler/SyslogUdpHandler.php @@ -25,12 +25,12 @@ class SyslogUdpHandler extends AbstractSyslogHandler protected $ident; /** - * @param string $host - * @param int $port - * @param mixed $facility - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param string $ident Program name or tag for each log message. + * @param string $host + * @param int $port + * @param mixed $facility + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $ident Program name or tag for each log message. */ public function __construct($host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $ident = 'php') { diff --git a/src/Monolog/Handler/WhatFailureGroupHandler.php b/src/Monolog/Handler/WhatFailureGroupHandler.php index 2732ba3d..6bc4671c 100644 --- a/src/Monolog/Handler/WhatFailureGroupHandler.php +++ b/src/Monolog/Handler/WhatFailureGroupHandler.php @@ -48,6 +48,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); diff --git a/src/Monolog/Logger.php b/src/Monolog/Logger.php index 49d00af1..5034ead1 100644 --- a/src/Monolog/Logger.php +++ b/src/Monolog/Logger.php @@ -15,6 +15,7 @@ use Monolog\Handler\HandlerInterface; use Monolog\Handler\StreamHandler; use Psr\Log\LoggerInterface; use Psr\Log\InvalidArgumentException; +use Exception; /** * Monolog log channel @@ -133,6 +134,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. @@ -141,7 +147,7 @@ class Logger implements LoggerInterface public function __construct($name, array $handlers = array(), array $processors = array()) { $this->name = $name; - $this->handlers = $handlers; + $this->setHandlers($handlers); $this->processors = $processors; } @@ -281,7 +287,7 @@ class Logger implements LoggerInterface * @param int $level The logging level * @param string $message The log message * @param array $context The log context - * @return Boolean Whether the record has been processed + * @return bool Whether the record has been processed */ public function addRecord($level, $message, array $context = array()) { @@ -329,16 +335,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 $e) { + $this->handleException($e, $record); } return true; @@ -347,9 +357,9 @@ class Logger implements LoggerInterface /** * Adds a log record at the DEBUG level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addDebug($message, array $context = array()) { @@ -359,9 +369,9 @@ class Logger implements LoggerInterface /** * Adds a log record at the INFO level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addInfo($message, array $context = array()) { @@ -371,9 +381,9 @@ class Logger implements LoggerInterface /** * Adds a log record at the NOTICE level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addNotice($message, array $context = array()) { @@ -383,9 +393,9 @@ class Logger implements LoggerInterface /** * Adds a log record at the WARNING level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addWarning($message, array $context = array()) { @@ -395,9 +405,9 @@ class Logger implements LoggerInterface /** * Adds a log record at the ERROR level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addError($message, array $context = array()) { @@ -407,9 +417,9 @@ class Logger implements LoggerInterface /** * Adds a log record at the CRITICAL level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addCritical($message, array $context = array()) { @@ -419,9 +429,9 @@ class Logger implements LoggerInterface /** * Adds a log record at the ALERT level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addAlert($message, array $context = array()) { @@ -431,9 +441,9 @@ class Logger implements LoggerInterface /** * Adds a log record at the EMERGENCY level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addEmergency($message, array $context = array()) { @@ -484,7 +494,7 @@ class Logger implements LoggerInterface * Checks whether the Logger has a handler that listens on the given level * * @param int $level - * @return Boolean + * @return bool */ public function isHandling($level) { @@ -501,15 +511,52 @@ 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. + */ + protected function handleException(Exception $e, array $record) + { + if (!$this->exceptionHandler) { + throw $e; + } + + call_user_func($this->exceptionHandler, $e, $record); + } + /** * Adds a log record at an arbitrary level. * * This method allows for compatibility with common interfaces. * * @param mixed $level The log level - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function log($level, $message, array $context = array()) { @@ -523,9 +570,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function debug($message, array $context = array()) { @@ -537,9 +584,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function info($message, array $context = array()) { @@ -551,9 +598,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function notice($message, array $context = array()) { @@ -565,9 +612,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function warn($message, array $context = array()) { @@ -579,9 +626,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function warning($message, array $context = array()) { @@ -593,9 +640,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function err($message, array $context = array()) { @@ -607,9 +654,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function error($message, array $context = array()) { @@ -621,9 +668,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function crit($message, array $context = array()) { @@ -635,9 +682,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function critical($message, array $context = array()) { @@ -649,9 +696,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function alert($message, array $context = array()) { @@ -663,9 +710,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function emerg($message, array $context = array()) { @@ -677,9 +724,9 @@ class Logger implements LoggerInterface * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function emergency($message, array $context = array()) { diff --git a/src/Monolog/Processor/MemoryProcessor.php b/src/Monolog/Processor/MemoryProcessor.php index 85f9dc5e..73f132d7 100644 --- a/src/Monolog/Processor/MemoryProcessor.php +++ b/src/Monolog/Processor/MemoryProcessor.php @@ -34,8 +34,8 @@ abstract class MemoryProcessor */ public function __construct($realUsage = true, $useFormatting = true) { - $this->realUsage = (boolean) $realUsage; - $this->useFormatting = (boolean) $useFormatting; + $this->realUsage = (bool) $realUsage; + $this->useFormatting = (bool) $useFormatting; } /** diff --git a/tests/Monolog/Formatter/FluentdFormatterTest.php b/tests/Monolog/Formatter/FluentdFormatterTest.php index 622b2bae..fd36dbcf 100644 --- a/tests/Monolog/Formatter/FluentdFormatterTest.php +++ b/tests/Monolog/Formatter/FluentdFormatterTest.php @@ -40,7 +40,7 @@ class FluentdFormatterTest extends TestCase $formatter = new FluentdFormatter(); $this->assertEquals( - '["test",0,{"message":"test","extra":[],"level":300,"level_name":"WARNING"}]', + '["test",0,{"message":"test","context":[],"extra":[],"level":300,"level_name":"WARNING"}]', $formatter->format($record) ); } @@ -55,7 +55,7 @@ class FluentdFormatterTest extends TestCase $formatter = new FluentdFormatter(true); $this->assertEquals( - '["test.error",0,{"message":"test","extra":[]}]', + '["test.error",0,{"message":"test","context":[],"extra":[]}]', $formatter->format($record) ); } diff --git a/tests/Monolog/Formatter/JsonFormatterTest.php b/tests/Monolog/Formatter/JsonFormatterTest.php index c9445f36..24b06cc9 100644 --- a/tests/Monolog/Formatter/JsonFormatterTest.php +++ b/tests/Monolog/Formatter/JsonFormatterTest.php @@ -180,4 +180,40 @@ class JsonFormatterTest extends TestCase '}'; return $formattedException; } + + public function testNormalizeHandleLargeArraysWithExactly1000Items() + { + $formatter = new NormalizerFormatter(); + $largeArray = range(1, 1000); + + $res = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array($largeArray), + 'datetime' => new \DateTime, + 'extra' => array(), + )); + + $this->assertCount(1000, $res['context'][0]); + $this->assertArrayNotHasKey('...', $res['context'][0]); + } + + public function testNormalizeHandleLargeArrays() + { + $formatter = new NormalizerFormatter(); + $largeArray = range(1, 2000); + + $res = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array($largeArray), + 'datetime' => new \DateTime, + 'extra' => array(), + )); + + $this->assertCount(1001, $res['context'][0]); + $this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']); + } } diff --git a/tests/Monolog/Formatter/NormalizerFormatterTest.php b/tests/Monolog/Formatter/NormalizerFormatterTest.php index 57bcdf98..b4f82897 100644 --- a/tests/Monolog/Formatter/NormalizerFormatterTest.php +++ b/tests/Monolog/Formatter/NormalizerFormatterTest.php @@ -193,6 +193,15 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase $this->assertEquals(@json_encode(array($foo, $bar)), $res); } + public function testCanNormalizeReferences() + { + $formatter = new NormalizerFormatter(); + $x = array('foo' => 'bar'); + $y = array('x' => &$x); + $x['y'] = &$y; + $formatter->format($y); + } + public function testIgnoresInvalidTypes() { // set up the recursion @@ -217,6 +226,24 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase $this->assertEquals(@json_encode(array($resource)), $res); } + public function testNormalizeHandleLargeArraysWithExactly1000Items() + { + $formatter = new NormalizerFormatter(); + $largeArray = range(1, 1000); + + $res = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array($largeArray), + 'datetime' => new \DateTime, + 'extra' => array(), + )); + + $this->assertCount(1000, $res['context'][0]); + $this->assertArrayNotHasKey('...', $res['context'][0]); + } + public function testNormalizeHandleLargeArrays() { $formatter = new NormalizerFormatter(); @@ -231,7 +258,7 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase 'extra' => array(), )); - $this->assertCount(1000, $res['context'][0]); + $this->assertCount(1001, $res['context'][0]); $this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']); } diff --git a/tests/Monolog/Handler/InsightOpsHandlerTest.php b/tests/Monolog/Handler/InsightOpsHandlerTest.php new file mode 100644 index 00000000..97c18b59 --- /dev/null +++ b/tests/Monolog/Handler/InsightOpsHandlerTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + namespace Monolog\Handler; + + use Monolog\TestCase; + use Monolog\Logger; + +/** + * @author Robert Kaufmann III + * @author Gabriel Machado + */ +class InsightOpsHandlerTest extends TestCase +{ + /** + * @var resource + */ + private $resource; + + /** + * @var LogEntriesHandler + */ + private $handler; + + public function testWriteContent() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Critical write test')); + + fseek($this->resource, 0); + $content = fread($this->resource, 1024); + + $this->assertRegexp('/testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] test.CRITICAL: Critical write test/', $content); + } + + public function testWriteBatchContent() + { + $this->createHandler(); + $this->handler->handleBatch($this->getMultipleRecords()); + + fseek($this->resource, 0); + $content = fread($this->resource, 1024); + + $this->assertRegexp('/(testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] .* \[\] \[\]\n){3}/', $content); + } + + private function createHandler() + { + $useSSL = extension_loaded('openssl'); + $args = array('testToken', 'us', $useSSL, Logger::DEBUG, true); + $this->resource = fopen('php://memory', 'a'); + $this->handler = $this->getMock( + '\Monolog\Handler\InsightOpsHandler', + array('fsockopen', 'streamSetTimeout', 'closeSocket'), + $args + ); + + $reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->handler, 'localhost:1234'); + + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->resource)); + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + $this->handler->expects($this->any()) + ->method('closeSocket') + ->will($this->returnValue(true)); + } +} diff --git a/tests/Monolog/Handler/RotatingFileHandlerTest.php b/tests/Monolog/Handler/RotatingFileHandlerTest.php index f1feb228..c6f5fac9 100644 --- a/tests/Monolog/Handler/RotatingFileHandlerTest.php +++ b/tests/Monolog/Handler/RotatingFileHandlerTest.php @@ -191,6 +191,40 @@ class RotatingFileHandlerTest extends TestCase ); } + /** + * @dataProvider rotationWhenSimilarFilesExistTests + */ + public function testRotationWhenSimilarFileNamesExist($dateFormat) + { + touch($old1 = __DIR__.'/Fixtures/foo-foo-'.date($dateFormat).'.rot'); + touch($old2 = __DIR__.'/Fixtures/foo-bar-'.date($dateFormat).'.rot'); + + $log = __DIR__.'/Fixtures/foo-'.date($dateFormat).'.rot'; + + $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2); + $handler->setFormatter($this->getIdentityFormatter()); + $handler->setFilenameFormat('{filename}-{date}', $dateFormat); + $handler->handle($this->getRecord()); + $handler->close(); + + $this->assertTrue(file_exists($log)); + } + + public function rotationWhenSimilarFilesExistTests() + { + + return array( + 'Rotation is triggered when the file of the current day is not present but similar exists' + => array(RotatingFileHandler::FILE_PER_DAY), + + 'Rotation is triggered when the file of the current month is not present but similar exists' + => array(RotatingFileHandler::FILE_PER_MONTH), + + 'Rotation is triggered when the file of the current year is not present but similar exists' + => array(RotatingFileHandler::FILE_PER_YEAR), + ); + } + public function testReuseCurrentFile() { $log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot'; diff --git a/tests/Monolog/Handler/Slack/SlackRecordTest.php b/tests/Monolog/Handler/Slack/SlackRecordTest.php index e1aa96d7..b9de7367 100644 --- a/tests/Monolog/Handler/Slack/SlackRecordTest.php +++ b/tests/Monolog/Handler/Slack/SlackRecordTest.php @@ -320,12 +320,12 @@ class SlackRecordTest extends TestCase 'short' => false, ), array( - 'title' => 'tags', + 'title' => 'Tags', 'value' => sprintf('```%s```', json_encode($extra['tags'])), 'short' => false ), array( - 'title' => 'test', + 'title' => 'Test', 'value' => $context['test'], 'short' => false ) @@ -353,6 +353,14 @@ class SlackRecordTest extends TestCase $this->assertSame($record['datetime']->getTimestamp(), $attachment['ts']); } + public function testContextHasException() + { + $record = $this->getRecord(Logger::CRITICAL, 'This is a critical message.', array('exception' => new \Exception())); + $slackRecord = new SlackRecord(null, null, true, null, false, true); + $data = $slackRecord->getSlackData($record); + $this->assertInternalType('string', $data['attachments'][0]['fields'][1]['value']); + } + public function testExcludeExtraAndContextFields() { $record = $this->getRecord( @@ -368,12 +376,12 @@ class SlackRecordTest extends TestCase $expected = array( array( - 'title' => 'info', + 'title' => 'Info', 'value' => sprintf('```%s```', json_encode(array('author' => 'Jordi'), $this->jsonPrettyPrintFlag)), 'short' => false ), array( - 'title' => 'tags', + 'title' => 'Tags', 'value' => sprintf('```%s```', json_encode(array('web'))), 'short' => false ), diff --git a/tests/Monolog/Handler/SocketHandlerTest.php b/tests/Monolog/Handler/SocketHandlerTest.php index 1f9c1f28..1da987c9 100644 --- a/tests/Monolog/Handler/SocketHandlerTest.php +++ b/tests/Monolog/Handler/SocketHandlerTest.php @@ -77,6 +77,13 @@ class SocketHandlerTest extends TestCase $this->assertEquals(10.25, $this->handler->getWritingTimeout()); } + public function testSetChunkSize() + { + $this->createHandler('localhost:1234'); + $this->handler->setChunkSize(1025); + $this->assertEquals(1025, $this->handler->getChunkSize()); + } + public function testSetConnectionString() { $this->createHandler('tcp://localhost:9090'); @@ -120,6 +127,19 @@ class SocketHandlerTest extends TestCase $this->writeRecord('Hello world'); } + /** + * @expectedException UnexpectedValueException + */ + public function testExceptionIsThrownIfCannotSetChunkSize() + { + $this->setMockHandler(array('streamSetChunkSize')); + $this->handler->setChunkSize(8192); + $this->handler->expects($this->once()) + ->method('streamSetChunkSize') + ->will($this->returnValue(false)); + $this->writeRecord('Hello world'); + } + /** * @expectedException RuntimeException */ @@ -304,6 +324,12 @@ class SocketHandlerTest extends TestCase ->will($this->returnValue(true)); } + if (!in_array('streamSetChunkSize', $methods)) { + $this->handler->expects($this->any()) + ->method('streamSetChunkSize') + ->will($this->returnValue(8192)); + } + $this->handler->setFormatter($this->getIdentityFormatter()); } } diff --git a/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php b/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php index 8d37a1fc..0594a232 100644 --- a/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php +++ b/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 */ diff --git a/tests/Monolog/LoggerTest.php b/tests/Monolog/LoggerTest.php index 1ecc34a0..f938ea02 100644 --- a/tests/Monolog/LoggerTest.php +++ b/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'); + } }