Skip to content

Commit

Permalink
Register signal handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
RGustBardon committed Feb 22, 2018
1 parent fd8c787 commit cf97984
Show file tree
Hide file tree
Showing 3 changed files with 347 additions and 3 deletions.
3 changes: 2 additions & 1 deletion doc/03-utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
can then statically access from anywhere. It is not really a best practice but can
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.
a Logger instance as an exception handler, error handler, fatal error handler or
signal handler.
- _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log
level is reached.
- _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain
Expand Down
80 changes: 79 additions & 1 deletion src/Monolog/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Monolog\Handler\AbstractHandler;
use ReflectionExtension;

/**
* Monolog error handler
*
* A facility to enable logging of runtime errors, exceptions and fatal errors.
* A facility to enable logging of runtime errors, exceptions, fatal errors and signals.
*
* Quick setup: <code>ErrorHandler::register($logger);</code>
*
Expand All @@ -40,6 +41,10 @@ class ErrorHandler
private $reservedMemory;
private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR);

private $previousSignalHandler = array();
private $signalLevelMap = array();
private $signalRestartSyscalls = array();

public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
Expand Down Expand Up @@ -104,6 +109,37 @@ public function registerFatalHandler($level = null, $reservedMemorySize = 20)
$this->hasFatalErrorHandler = true;
}

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;
}

protected function defaultErrorLevelMap()
{
return array(
Expand Down Expand Up @@ -190,6 +226,48 @@ public function handleFatalError()
}
}

public function handleSignal($signo, array $siginfo = null)
{
static $signals = array();

if (!$signals && extension_loaded('pcntl')) {
$pcntl = new ReflectionExtension('pcntl');
foreach ($pcntl->getConstants() as $name => $value) {
if (substr($name, 0, 3) === 'SIG' && $name[3] !== '_') {
$signals[$value] = $name;
}
}
}

$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 >= 71000) {
$this->previousSignalHandler[$signo]($signo, $siginfo);
} else {
$this->previousSignalHandler[$signo]($signo);
}
}
}

private static function codeToString($code)
{
switch ($code) {
Expand Down
Loading

0 comments on commit cf97984

Please sign in to comment.