mirror of
https://github.com/Seldaek/monolog.git
synced 2025-08-06 05:07:36 +02:00
Merge branch '1.x'
This commit is contained in:
@@ -19,6 +19,8 @@
|
||||
* Fixed table row styling issues in HtmlFormatter
|
||||
* Fixed RavenHandler dropping the message when logging exception
|
||||
* Fixed WhatFailureGroupHandler skipping processors when using handleBatch
|
||||
* Added a `ResettableInterface` in order to reset/reset/clear/flush handlers and processors
|
||||
and implement it where possible
|
||||
|
||||
### 1.23.0 (2017-06-19)
|
||||
|
||||
|
@@ -5,6 +5,8 @@
|
||||
help in some older codebases or for ease of use.
|
||||
- _ErrorHandler_: The `Monolog\ErrorHandler` class allows you to easily register
|
||||
a Logger instance as an exception handler, error handler or fatal error handler.
|
||||
- _SignalHandler_: The `Monolog\SignalHandler` class allows you to easily register
|
||||
a Logger instance as a POSIX signal handler.
|
||||
- _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log
|
||||
level is reached.
|
||||
- _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain
|
||||
|
@@ -13,6 +13,7 @@ namespace Monolog;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use Monolog\Registry;
|
||||
|
||||
/**
|
||||
* Monolog error handler
|
||||
@@ -163,7 +164,7 @@ class ErrorHandler
|
||||
|
||||
$this->logger->log(
|
||||
$level,
|
||||
sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()),
|
||||
sprintf('Uncaught Exception %s: "%s" at %s line %s', Utils::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine()),
|
||||
['exception' => $e]
|
||||
);
|
||||
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Monolog\Formatter;
|
||||
|
||||
use Monolog\Utils;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
@@ -166,7 +167,7 @@ class JsonFormatter extends NormalizerFormatter
|
||||
protected function normalizeException(Throwable $e, int $depth = 0): array
|
||||
{
|
||||
$data = [
|
||||
'class' => get_class($e),
|
||||
'class' => Utils::getClass($e),
|
||||
'message' => $e->getMessage(),
|
||||
'code' => $e->getCode(),
|
||||
'file' => $e->getFile().':'.$e->getLine(),
|
||||
|
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Monolog\Formatter;
|
||||
|
||||
use Monolog\Utils;
|
||||
|
||||
/**
|
||||
* Formats incoming records into a one-line string
|
||||
*
|
||||
@@ -168,7 +170,7 @@ class LineFormatter extends NormalizerFormatter
|
||||
|
||||
private function formatException(\Throwable $e): string
|
||||
{
|
||||
$str = '[object] (' . get_class($e) . '(code: ' . $e->getCode() . '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . ')';
|
||||
$str = '[object] (' . Utils::getClass($e) . '(code: ' . $e->getCode() . '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . ')';
|
||||
if ($this->includeStacktraces) {
|
||||
$str .= "\n[stacktrace]\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Monolog\Formatter;
|
||||
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use Monolog\Utils;
|
||||
|
||||
/**
|
||||
* Formats a record for use with the MongoDBHandler.
|
||||
@@ -83,7 +84,7 @@ class MongoDBFormatter implements FormatterInterface
|
||||
protected function formatObject($value, int $nestingLevel)
|
||||
{
|
||||
$objectVars = get_object_vars($value);
|
||||
$objectVars['class'] = get_class($value);
|
||||
$objectVars['class'] = Utils::getClass($value);
|
||||
|
||||
return $this->formatArray($objectVars, $nestingLevel);
|
||||
}
|
||||
@@ -91,7 +92,7 @@ class MongoDBFormatter implements FormatterInterface
|
||||
protected function formatException(\Throwable $exception, int $nestingLevel)
|
||||
{
|
||||
$formattedException = [
|
||||
'class' => get_class($exception),
|
||||
'class' => Utils::getClass($exception),
|
||||
'message' => $exception->getMessage(),
|
||||
'code' => $exception->getCode(),
|
||||
'file' => $exception->getFile() . ':' . $exception->getLine(),
|
||||
|
@@ -13,6 +13,7 @@ namespace Monolog\Formatter;
|
||||
|
||||
use Throwable;
|
||||
use Monolog\DateTimeImmutable;
|
||||
use Monolog\Utils;
|
||||
|
||||
/**
|
||||
* Normalizes incoming records to remove objects/resources so it's easier to dump to various targets
|
||||
@@ -146,7 +147,7 @@ class NormalizerFormatter implements FormatterInterface
|
||||
}
|
||||
}
|
||||
|
||||
return [get_class($data) => $value];
|
||||
return [Utils::getClass($data) => $value];
|
||||
}
|
||||
|
||||
if (is_resource($data)) {
|
||||
@@ -162,7 +163,7 @@ class NormalizerFormatter implements FormatterInterface
|
||||
protected function normalizeException(Throwable $e, int $depth = 0)
|
||||
{
|
||||
$data = [
|
||||
'class' => get_class($e),
|
||||
'class' => Utils::getClass($e),
|
||||
'message' => $e->getMessage(),
|
||||
'code' => $e->getCode(),
|
||||
'file' => $e->getFile().':'.$e->getLine(),
|
||||
@@ -195,7 +196,7 @@ class NormalizerFormatter implements FormatterInterface
|
||||
// as a class name to avoid any unexpected leak of sensitive information
|
||||
$frame['args'] = array_map(function ($arg) {
|
||||
if (is_object($arg) && !$arg instanceof \DateTimeInterface) {
|
||||
return sprintf("[object] (%s)", get_class($arg));
|
||||
return sprintf("[object] (%s)", Utils::getClass($arg));
|
||||
}
|
||||
|
||||
return $arg;
|
||||
|
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Monolog\Handler;
|
||||
|
||||
use Monolog\ResettableInterface;
|
||||
|
||||
/**
|
||||
* Base Handler class providing the Handler structure
|
||||
*
|
||||
@@ -19,7 +21,7 @@ namespace Monolog\Handler;
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Christophe Coevoet <stof@notk.org>
|
||||
*/
|
||||
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
|
||||
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface
|
||||
{
|
||||
use ProcessableHandlerTrait;
|
||||
use FormattableHandlerTrait;
|
||||
@@ -48,4 +50,15 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
|
||||
* Writes the record down to the log of the implementing handler
|
||||
*/
|
||||
abstract protected function write(array $record): void;
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->close();
|
||||
|
||||
foreach ($this->processors as $processor) {
|
||||
if ($processor instanceof ResettableInterface) {
|
||||
$processor->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -70,14 +70,19 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||
} elseif ($format === 'js') {
|
||||
static::writeOutput(static::generateScript());
|
||||
}
|
||||
static::reset();
|
||||
static::resetStatic();
|
||||
}
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
self::resetStatic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget all logged records
|
||||
*/
|
||||
public static function reset(): void
|
||||
public static function resetStatic(): void
|
||||
{
|
||||
static::$records = [];
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Monolog\Handler;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\ResettableInterface;
|
||||
|
||||
/**
|
||||
* Buffers all records until closing the handler and then pass them as batch.
|
||||
@@ -114,4 +115,13 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
|
||||
$this->bufferSize = 0;
|
||||
$this->buffer = [];
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
if ($this->handler instanceof ResettableInterface) {
|
||||
$this->handler->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ namespace Monolog\Handler;
|
||||
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
|
||||
use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
|
||||
use Monolog\Logger;
|
||||
use Monolog\ResettableInterface;
|
||||
|
||||
/**
|
||||
* Buffers all records until a certain level is reached
|
||||
@@ -147,7 +148,14 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
$this->buffer = array();
|
||||
$this->buffering = true;
|
||||
|
||||
if ($this->handler instanceof ResettableInterface) {
|
||||
$this->handler->reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Monolog\Handler;
|
||||
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
use Monolog\ResettableInterface;
|
||||
|
||||
/**
|
||||
* Forwards records to multiple handlers
|
||||
@@ -90,6 +91,17 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface
|
||||
}
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
foreach ($this->handlers as $handler) {
|
||||
if ($handler instanceof ResettableInterface) {
|
||||
$handler->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Monolog\Handler;
|
||||
|
||||
use Monolog\ResettableInterface;
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
|
||||
/**
|
||||
@@ -30,7 +31,7 @@ use Monolog\Formatter\FormatterInterface;
|
||||
*
|
||||
* @author Alexey Karapetov <alexey@karapetov.com>
|
||||
*/
|
||||
class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface
|
||||
class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface
|
||||
{
|
||||
/**
|
||||
* @var HandlerInterface
|
||||
@@ -127,4 +128,11 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
|
||||
|
||||
throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class);
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
if ($this->handler instanceof ResettableInterface) {
|
||||
return $this->handler->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ use Raven_Client;
|
||||
|
||||
/**
|
||||
* Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server
|
||||
* using raven-php (https://github.com/getsentry/raven-php)
|
||||
* using sentry-php (https://github.com/getsentry/sentry-php)
|
||||
*
|
||||
* @author Marc Abramowitz <marc@marc-abramowitz.com>
|
||||
*/
|
||||
|
@@ -25,7 +25,7 @@ use Throwable;
|
||||
*
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class Logger implements LoggerInterface
|
||||
class Logger implements LoggerInterface, ResettableInterface
|
||||
{
|
||||
/**
|
||||
* Detailed debug information
|
||||
@@ -333,6 +333,21 @@ class Logger implements LoggerInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
foreach ($this->handlers as $handler) {
|
||||
if ($handler instanceof ResettableInterface) {
|
||||
$handler->reset();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->processors as $processor) {
|
||||
if ($processor instanceof ResettableInterface) {
|
||||
$processor->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all supported logging levels.
|
||||
*
|
||||
|
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Monolog\Processor;
|
||||
|
||||
use Monolog\Utils;
|
||||
|
||||
/**
|
||||
* Processes a record's message according to PSR-3 rules
|
||||
*
|
||||
@@ -66,7 +68,7 @@ class PsrLogMessageProcessor implements ProcessorInterface
|
||||
$replacements[$placeholder] = $val->format($this->dateFormat ?: static::SIMPLE_DATE);
|
||||
}
|
||||
} elseif (is_object($val)) {
|
||||
$replacements[$placeholder] = '[object '.get_class($val).']';
|
||||
$replacements[$placeholder] = '[object '.Utils::getClass($val).']';
|
||||
} elseif (is_array($val)) {
|
||||
$replacements[$placeholder] = 'array'.@json_encode($val);
|
||||
} else {
|
||||
|
@@ -11,12 +11,14 @@
|
||||
|
||||
namespace Monolog\Processor;
|
||||
|
||||
use Monolog\ResettableInterface;
|
||||
|
||||
/**
|
||||
* Adds a unique identifier into records
|
||||
*
|
||||
* @author Simon Mönch <sm@webfactory.de>
|
||||
*/
|
||||
class UidProcessor implements ProcessorInterface
|
||||
class UidProcessor implements ProcessorInterface, ResettableInterface
|
||||
{
|
||||
private $uid;
|
||||
|
||||
@@ -26,7 +28,7 @@ class UidProcessor implements ProcessorInterface
|
||||
throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32');
|
||||
}
|
||||
|
||||
$this->uid = substr(bin2hex(random_bytes((int) ceil($length / 2))), 0, $length);
|
||||
$this->uid = $this->generateUid($length);
|
||||
}
|
||||
|
||||
public function __invoke(array $record): array
|
||||
@@ -43,4 +45,14 @@ class UidProcessor implements ProcessorInterface
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->uid = $this->generateUid(strlen($this->uid));
|
||||
}
|
||||
|
||||
private function generateUid($length)
|
||||
{
|
||||
return substr(bin2hex(random_bytes((int) ceil($length / 2))), 0, $length);
|
||||
}
|
||||
}
|
||||
|
@@ -130,4 +130,14 @@ class Registry
|
||||
{
|
||||
return self::getInstance($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getClass($object)
|
||||
{
|
||||
$class = \get_class($object);
|
||||
|
||||
return 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
|
||||
}
|
||||
}
|
||||
|
25
src/Monolog/ResettableInterface.php
Normal file
25
src/Monolog/ResettableInterface.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Monolog package.
|
||||
*
|
||||
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Monolog;
|
||||
|
||||
/**
|
||||
* Handler or Processor implementing this interface will be reset when Logger::reset() is called.
|
||||
*
|
||||
* Resetting an Handler or a Processor usually means cleaning all buffers or
|
||||
* resetting in its internal state. This should also generally close() the handler.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
interface ResettableInterface
|
||||
{
|
||||
public function reset();
|
||||
}
|
115
src/Monolog/SignalHandler.php
Normal file
115
src/Monolog/SignalHandler.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Monolog package.
|
||||
*
|
||||
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Monolog;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use ReflectionExtension;
|
||||
|
||||
/**
|
||||
* Monolog POSIX signal handler
|
||||
*
|
||||
* @author Robert Gust-Bardon <robert@gust-bardon.org>
|
||||
*/
|
||||
class SignalHandler
|
||||
{
|
||||
private $logger;
|
||||
|
||||
private $previousSignalHandler = array();
|
||||
private $signalLevelMap = array();
|
||||
private $signalRestartSyscalls = array();
|
||||
|
||||
public function __construct(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function registerSignalHandler($signo, $level = LogLevel::CRITICAL, $callPrevious = true, $restartSyscalls = true, $async = true)
|
||||
{
|
||||
if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($callPrevious) {
|
||||
if (function_exists('pcntl_signal_get_handler')) {
|
||||
$handler = pcntl_signal_get_handler($signo);
|
||||
if ($handler === false) {
|
||||
return $this;
|
||||
}
|
||||
$this->previousSignalHandler[$signo] = $handler;
|
||||
} else {
|
||||
$this->previousSignalHandler[$signo] = true;
|
||||
}
|
||||
} else {
|
||||
unset($this->previousSignalHandler[$signo]);
|
||||
}
|
||||
$this->signalLevelMap[$signo] = $level;
|
||||
$this->signalRestartSyscalls[$signo] = $restartSyscalls;
|
||||
|
||||
if (function_exists('pcntl_async_signals') && $async !== null) {
|
||||
pcntl_async_signals($async);
|
||||
}
|
||||
|
||||
pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handleSignal($signo, array $siginfo = null)
|
||||
{
|
||||
static $signals = array();
|
||||
|
||||
if (!$signals && extension_loaded('pcntl')) {
|
||||
$pcntl = new ReflectionExtension('pcntl');
|
||||
$constants = $pcntl->getConstants();
|
||||
if (!$constants) {
|
||||
// HHVM 3.24.2 returns an empty array.
|
||||
$constants = get_defined_constants(true);
|
||||
$constants = $constants['Core'];
|
||||
}
|
||||
foreach ($constants as $name => $value) {
|
||||
if (substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && is_int($value)) {
|
||||
$signals[$value] = $name;
|
||||
}
|
||||
}
|
||||
unset($constants);
|
||||
}
|
||||
|
||||
$level = isset($this->signalLevelMap[$signo]) ? $this->signalLevelMap[$signo] : LogLevel::CRITICAL;
|
||||
$signal = isset($signals[$signo]) ? $signals[$signo] : $signo;
|
||||
$context = isset($siginfo) ? $siginfo : array();
|
||||
$this->logger->log($level, sprintf('Program received signal %s', $signal), $context);
|
||||
|
||||
if (!isset($this->previousSignalHandler[$signo])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->previousSignalHandler[$signo] === true || $this->previousSignalHandler[$signo] === SIG_DFL) {
|
||||
if (extension_loaded('pcntl') && function_exists('pcntl_signal') && function_exists('pcntl_sigprocmask') && function_exists('pcntl_signal_dispatch')
|
||||
&& extension_loaded('posix') && function_exists('posix_getpid') && function_exists('posix_kill')) {
|
||||
$restartSyscalls = isset($this->restartSyscalls[$signo]) ? $this->restartSyscalls[$signo] : true;
|
||||
pcntl_signal($signo, SIG_DFL, $restartSyscalls);
|
||||
pcntl_sigprocmask(SIG_UNBLOCK, array($signo), $oldset);
|
||||
posix_kill(posix_getpid(), $signo);
|
||||
pcntl_signal_dispatch();
|
||||
pcntl_sigprocmask(SIG_SETMASK, $oldset);
|
||||
pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls);
|
||||
}
|
||||
} elseif (is_callable($this->previousSignalHandler[$signo])) {
|
||||
if (PHP_VERSION_ID >= 70100) {
|
||||
$this->previousSignalHandler[$signo]($signo, $siginfo);
|
||||
} else {
|
||||
$this->previousSignalHandler[$signo]($signo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
src/Monolog/Utils.php
Normal file
25
src/Monolog/Utils.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Monolog package.
|
||||
*
|
||||
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Monolog;
|
||||
|
||||
class Utils
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getClass($object)
|
||||
{
|
||||
$class = \get_class($object);
|
||||
|
||||
return 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ class BrowserConsoleHandlerTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
BrowserConsoleHandler::reset();
|
||||
BrowserConsoleHandler::resetStatic();
|
||||
}
|
||||
|
||||
protected function generateScript()
|
||||
|
@@ -21,7 +21,7 @@ class ChromePHPHandlerTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
TestChromePHPHandler::reset();
|
||||
TestChromePHPHandler::resetStatic();
|
||||
$_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; Chrome/1.0';
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ class TestChromePHPHandler extends ChromePHPHandler
|
||||
{
|
||||
protected $headers = [];
|
||||
|
||||
public static function reset()
|
||||
public static function resetStatic()
|
||||
{
|
||||
self::$initialized = false;
|
||||
self::$overflowed = false;
|
||||
|
@@ -58,7 +58,7 @@ class FingersCrossedHandlerTest extends TestCase
|
||||
* @covers Monolog\Handler\FingersCrossedHandler::activate
|
||||
* @covers Monolog\Handler\FingersCrossedHandler::reset
|
||||
*/
|
||||
public function testHandleRestartBufferingAfterReset()
|
||||
public function testHandleResetBufferingAfterReset()
|
||||
{
|
||||
$test = new TestHandler();
|
||||
$handler = new FingersCrossedHandler($test);
|
||||
@@ -76,7 +76,7 @@ class FingersCrossedHandlerTest extends TestCase
|
||||
* @covers Monolog\Handler\FingersCrossedHandler::handle
|
||||
* @covers Monolog\Handler\FingersCrossedHandler::activate
|
||||
*/
|
||||
public function testHandleRestartBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled()
|
||||
public function testHandleResetBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled()
|
||||
{
|
||||
$test = new TestHandler();
|
||||
$handler = new FingersCrossedHandler($test, Logger::WARNING, 0, false, false);
|
||||
|
@@ -21,7 +21,7 @@ class FirePHPHandlerTest extends TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
TestFirePHPHandler::reset();
|
||||
TestFirePHPHandler::resetStatic();
|
||||
$_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; FirePHP/1.0';
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ class TestFirePHPHandler extends FirePHPHandler
|
||||
{
|
||||
protected $headers = [];
|
||||
|
||||
public static function reset()
|
||||
public static function resetStatic()
|
||||
{
|
||||
self::$initialized = false;
|
||||
self::$sendHeaders = true;
|
||||
|
@@ -635,4 +635,77 @@ class LoggerTest extends \PHPUnit\Framework\TestCase
|
||||
$logger->pushHandler($handler);
|
||||
$logger->info('test');
|
||||
}
|
||||
|
||||
public function testReset()
|
||||
{
|
||||
$logger = new Logger('app');
|
||||
|
||||
$testHandler = new Handler\TestHandler();
|
||||
$bufferHandler = new Handler\BufferHandler($testHandler);
|
||||
$groupHandler = new Handler\GroupHandler(array($bufferHandler));
|
||||
$fingersCrossedHandler = new Handler\FingersCrossedHandler($groupHandler);
|
||||
|
||||
$logger->pushHandler($fingersCrossedHandler);
|
||||
|
||||
$processorUid1 = new Processor\UidProcessor(10);
|
||||
$uid1 = $processorUid1->getUid();
|
||||
$groupHandler->pushProcessor($processorUid1);
|
||||
|
||||
$processorUid2 = new Processor\UidProcessor(5);
|
||||
$uid2 = $processorUid2->getUid();
|
||||
$logger->pushProcessor($processorUid2);
|
||||
|
||||
$getProperty = function ($object, $property) {
|
||||
$reflectionProperty = new \ReflectionProperty(get_class($object), $property);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
|
||||
return $reflectionProperty->getValue($object);
|
||||
};
|
||||
$that = $this;
|
||||
$assertBufferOfBufferHandlerEmpty = function () use ($getProperty, $bufferHandler, $that) {
|
||||
$that->assertEmpty($getProperty($bufferHandler, 'buffer'));
|
||||
};
|
||||
$assertBuffersEmpty = function() use ($assertBufferOfBufferHandlerEmpty, $getProperty, $fingersCrossedHandler, $that) {
|
||||
$assertBufferOfBufferHandlerEmpty();
|
||||
$that->assertEmpty($getProperty($fingersCrossedHandler, 'buffer'));
|
||||
};
|
||||
|
||||
$logger->debug('debug');
|
||||
$logger->reset();
|
||||
$assertBuffersEmpty();
|
||||
$this->assertFalse($testHandler->hasDebugRecords());
|
||||
$this->assertFalse($testHandler->hasErrorRecords());
|
||||
$this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
|
||||
$this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
|
||||
|
||||
$logger->debug('debug');
|
||||
$logger->error('error');
|
||||
$logger->reset();
|
||||
$assertBuffersEmpty();
|
||||
$this->assertTrue($testHandler->hasDebugRecords());
|
||||
$this->assertTrue($testHandler->hasErrorRecords());
|
||||
$this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
|
||||
$this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
|
||||
|
||||
$logger->info('info');
|
||||
$this->assertNotEmpty($getProperty($fingersCrossedHandler, 'buffer'));
|
||||
$assertBufferOfBufferHandlerEmpty();
|
||||
$this->assertFalse($testHandler->hasInfoRecords());
|
||||
|
||||
$logger->reset();
|
||||
$assertBuffersEmpty();
|
||||
$this->assertFalse($testHandler->hasInfoRecords());
|
||||
$this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
|
||||
$this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
|
||||
|
||||
$logger->notice('notice');
|
||||
$logger->emergency('emergency');
|
||||
$logger->reset();
|
||||
$assertBuffersEmpty();
|
||||
$this->assertFalse($testHandler->hasInfoRecords());
|
||||
$this->assertTrue($testHandler->hasNoticeRecords());
|
||||
$this->assertTrue($testHandler->hasEmergencyRecords());
|
||||
$this->assertNotSame($uid1, $processorUid1->getUid());
|
||||
$this->assertNotSame($uid2, $processorUid2->getUid());
|
||||
}
|
||||
}
|
||||
|
287
tests/Monolog/SignalHandlerTest.php
Normal file
287
tests/Monolog/SignalHandlerTest.php
Normal file
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Monolog package.
|
||||
*
|
||||
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Monolog;
|
||||
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\TestHandler;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
/**
|
||||
* @author Robert Gust-Bardon <robert@gust-bardon.org>
|
||||
* @covers Monolog\SignalHandler
|
||||
*/
|
||||
class SignalHandlerTest extends TestCase
|
||||
{
|
||||
|
||||
private $asyncSignalHandling;
|
||||
private $blockedSignals;
|
||||
private $signalHandlers;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->signalHandlers = array();
|
||||
if (extension_loaded('pcntl')) {
|
||||
if (function_exists('pcntl_async_signals')) {
|
||||
$this->asyncSignalHandling = pcntl_async_signals();
|
||||
}
|
||||
if (function_exists('pcntl_sigprocmask')) {
|
||||
pcntl_sigprocmask(SIG_BLOCK, array(), $this->blockedSignals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
if ($this->asyncSignalHandling !== null) {
|
||||
pcntl_async_signals($this->asyncSignalHandling);
|
||||
}
|
||||
if ($this->blockedSignals !== null) {
|
||||
pcntl_sigprocmask(SIG_SETMASK, $this->blockedSignals);
|
||||
}
|
||||
if ($this->signalHandlers) {
|
||||
pcntl_signal_dispatch();
|
||||
foreach ($this->signalHandlers as $signo => $handler) {
|
||||
pcntl_signal($signo, $handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setSignalHandler($signo, $handler = SIG_DFL) {
|
||||
if (function_exists('pcntl_signal_get_handler')) {
|
||||
$this->signalHandlers[$signo] = pcntl_signal_get_handler($signo);
|
||||
} else {
|
||||
$this->signalHandlers[$signo] = SIG_DFL;
|
||||
}
|
||||
$this->assertTrue(pcntl_signal($signo, $handler));
|
||||
}
|
||||
|
||||
public function testHandleSignal()
|
||||
{
|
||||
$logger = new Logger('test', array($handler = new TestHandler));
|
||||
$errHandler = new SignalHandler($logger);
|
||||
$signo = 2; // SIGINT.
|
||||
$siginfo = array('signo' => $signo, 'errno' => 0, 'code' => 0);
|
||||
$errHandler->handleSignal($signo, $siginfo);
|
||||
$this->assertCount(1, $handler->getRecords());
|
||||
$this->assertTrue($handler->hasCriticalRecords());
|
||||
$records = $handler->getRecords();
|
||||
$this->assertSame($siginfo, $records[0]['context']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testHandleSignal
|
||||
* @requires extension pcntl
|
||||
* @requires extension posix
|
||||
* @requires function pcntl_signal
|
||||
* @requires function pcntl_signal_dispatch
|
||||
* @requires function posix_getpid
|
||||
* @requires function posix_kill
|
||||
*/
|
||||
public function testRegisterSignalHandler()
|
||||
{
|
||||
// SIGCONT and SIGURG should be ignored by default.
|
||||
if (!defined('SIGCONT') || !defined('SIGURG')) {
|
||||
$this->markTestSkipped('This test requires the SIGCONT and SIGURG pcntl constants.');
|
||||
}
|
||||
|
||||
$this->setSignalHandler(SIGCONT, SIG_IGN);
|
||||
$this->setSignalHandler(SIGURG, SIG_IGN);
|
||||
|
||||
$logger = new Logger('test', array($handler = new TestHandler));
|
||||
$errHandler = new SignalHandler($logger);
|
||||
$pid = posix_getpid();
|
||||
|
||||
$this->assertTrue(posix_kill($pid, SIGURG));
|
||||
$this->assertTrue(pcntl_signal_dispatch());
|
||||
$this->assertCount(0, $handler->getRecords());
|
||||
|
||||
$errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, false, false);
|
||||
|
||||
$this->assertTrue(posix_kill($pid, SIGCONT));
|
||||
$this->assertTrue(pcntl_signal_dispatch());
|
||||
$this->assertCount(0, $handler->getRecords());
|
||||
|
||||
$this->assertTrue(posix_kill($pid, SIGURG));
|
||||
$this->assertTrue(pcntl_signal_dispatch());
|
||||
$this->assertCount(1, $handler->getRecords());
|
||||
$this->assertTrue($handler->hasInfoThatContains('SIGURG'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider defaultPreviousProvider
|
||||
* @depends testRegisterSignalHandler
|
||||
* @requires function pcntl_fork
|
||||
* @requires function pcntl_sigprocmask
|
||||
* @requires function pcntl_waitpid
|
||||
*/
|
||||
public function testRegisterDefaultPreviousSignalHandler($signo, $callPrevious, $expected)
|
||||
{
|
||||
$this->setSignalHandler($signo, SIG_DFL);
|
||||
|
||||
$path = tempnam(sys_get_temp_dir(), 'monolog-');
|
||||
$this->assertNotFalse($path);
|
||||
|
||||
$pid = pcntl_fork();
|
||||
if ($pid === 0) { // Child.
|
||||
$streamHandler = new StreamHandler($path);
|
||||
$streamHandler->setFormatter($this->getIdentityFormatter());
|
||||
$logger = new Logger('test', array($streamHandler));
|
||||
$errHandler = new SignalHandler($logger);
|
||||
$errHandler->registerSignalHandler($signo, LogLevel::INFO, $callPrevious, false, false);
|
||||
pcntl_sigprocmask(SIG_SETMASK, array(SIGCONT));
|
||||
posix_kill(posix_getpid(), $signo);
|
||||
pcntl_signal_dispatch();
|
||||
// If $callPrevious is true, SIGINT should terminate by this line.
|
||||
pcntl_sigprocmask(SIG_BLOCK, array(), $oldset);
|
||||
file_put_contents($path, implode(' ', $oldset), FILE_APPEND);
|
||||
posix_kill(posix_getpid(), $signo);
|
||||
pcntl_signal_dispatch();
|
||||
exit();
|
||||
}
|
||||
|
||||
$this->assertNotSame(-1, $pid);
|
||||
$this->assertNotSame(-1, pcntl_waitpid($pid, $status));
|
||||
$this->assertNotSame(-1, $status);
|
||||
$this->assertSame($expected, file_get_contents($path));
|
||||
}
|
||||
|
||||
public function defaultPreviousProvider()
|
||||
{
|
||||
if (!defined('SIGCONT') || !defined('SIGINT') || !defined('SIGURG')) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array(
|
||||
array(SIGINT, false, 'Program received signal SIGINT'.SIGCONT.'Program received signal SIGINT'),
|
||||
array(SIGINT, true, 'Program received signal SIGINT'),
|
||||
array(SIGURG, false, 'Program received signal SIGURG'.SIGCONT.'Program received signal SIGURG'),
|
||||
array(SIGURG, true, 'Program received signal SIGURG'.SIGCONT.'Program received signal SIGURG'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider callablePreviousProvider
|
||||
* @depends testRegisterSignalHandler
|
||||
* @requires function pcntl_signal_get_handler
|
||||
*/
|
||||
public function testRegisterCallablePreviousSignalHandler($callPrevious)
|
||||
{
|
||||
$this->setSignalHandler(SIGURG, SIG_IGN);
|
||||
|
||||
$logger = new Logger('test', array($handler = new TestHandler));
|
||||
$errHandler = new SignalHandler($logger);
|
||||
$previousCalled = 0;
|
||||
pcntl_signal(SIGURG, function ($signo, array $siginfo = null) use (&$previousCalled) {
|
||||
++$previousCalled;
|
||||
});
|
||||
$errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, $callPrevious, false, false);
|
||||
$this->assertTrue(posix_kill(posix_getpid(), SIGURG));
|
||||
$this->assertTrue(pcntl_signal_dispatch());
|
||||
$this->assertCount(1, $handler->getRecords());
|
||||
$this->assertTrue($handler->hasInfoThatContains('SIGURG'));
|
||||
$this->assertSame($callPrevious ? 1 : 0, $previousCalled);
|
||||
}
|
||||
|
||||
public function callablePreviousProvider()
|
||||
{
|
||||
return array(
|
||||
array(false),
|
||||
array(true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider restartSyscallsProvider
|
||||
* @depends testRegisterDefaultPreviousSignalHandler
|
||||
* @requires function pcntl_fork
|
||||
* @requires function pcntl_waitpid
|
||||
*/
|
||||
public function testRegisterSyscallRestartingSignalHandler($restartSyscalls)
|
||||
{
|
||||
$this->setSignalHandler(SIGURG, SIG_IGN);
|
||||
|
||||
$parentPid = posix_getpid();
|
||||
$microtime = microtime(true);
|
||||
|
||||
$pid = pcntl_fork();
|
||||
if ($pid === 0) { // Child.
|
||||
usleep(100000);
|
||||
posix_kill($parentPid, SIGURG);
|
||||
usleep(100000);
|
||||
exit();
|
||||
}
|
||||
|
||||
$this->assertNotSame(-1, $pid);
|
||||
$logger = new Logger('test', array($handler = new TestHandler));
|
||||
$errHandler = new SignalHandler($logger);
|
||||
$errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, $restartSyscalls, false);
|
||||
if ($restartSyscalls) {
|
||||
// pcntl_wait is expected to be restarted after the signal handler.
|
||||
$this->assertNotSame(-1, pcntl_waitpid($pid, $status));
|
||||
} else {
|
||||
// pcntl_wait is expected to be interrupted when the signal handler is invoked.
|
||||
$this->assertSame(-1, pcntl_waitpid($pid, $status));
|
||||
}
|
||||
$this->assertSame($restartSyscalls, microtime(true) - $microtime > 0.15);
|
||||
$this->assertTrue(pcntl_signal_dispatch());
|
||||
$this->assertCount(1, $handler->getRecords());
|
||||
if ($restartSyscalls) {
|
||||
// The child has already exited.
|
||||
$this->assertSame(-1, pcntl_waitpid($pid, $status));
|
||||
} else {
|
||||
// The child has not exited yet.
|
||||
$this->assertNotSame(-1, pcntl_waitpid($pid, $status));
|
||||
}
|
||||
}
|
||||
|
||||
public function restartSyscallsProvider()
|
||||
{
|
||||
return array(
|
||||
array(false),
|
||||
array(true),
|
||||
array(false),
|
||||
array(true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider asyncProvider
|
||||
* @depends testRegisterDefaultPreviousSignalHandler
|
||||
* @requires function pcntl_async_signals
|
||||
*/
|
||||
public function testRegisterAsyncSignalHandler($initialAsync, $desiredAsync, $expectedBefore, $expectedAfter)
|
||||
{
|
||||
$this->setSignalHandler(SIGURG, SIG_IGN);
|
||||
pcntl_async_signals($initialAsync);
|
||||
|
||||
$logger = new Logger('test', array($handler = new TestHandler));
|
||||
$errHandler = new SignalHandler($logger);
|
||||
$errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, false, $desiredAsync);
|
||||
$this->assertTrue(posix_kill(posix_getpid(), SIGURG));
|
||||
$this->assertCount($expectedBefore, $handler->getRecords());
|
||||
$this->assertTrue(pcntl_signal_dispatch());
|
||||
$this->assertCount($expectedAfter, $handler->getRecords());
|
||||
}
|
||||
|
||||
public function asyncProvider()
|
||||
{
|
||||
return array(
|
||||
array(false, false, 0, 1),
|
||||
array(false, null, 0, 1),
|
||||
array(false, true, 1, 1),
|
||||
array(true, false, 0, 1),
|
||||
array(true, null, 1, 1),
|
||||
array(true, true, 1, 1),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user