vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php line 41

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. /*
  3.  * This file is part of the Monolog package.
  4.  *
  5.  * (c) Jordi Boggiano <j.boggiano@seld.be>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Monolog\Handler;
  11. use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
  12. use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
  13. use Monolog\Logger;
  14. use Monolog\ResettableInterface;
  15. use Monolog\Formatter\FormatterInterface;
  16. use Psr\Log\LogLevel;
  17. /**
  18.  * Buffers all records until a certain level is reached
  19.  *
  20.  * The advantage of this approach is that you don't get any clutter in your log files.
  21.  * Only requests which actually trigger an error (or whatever your actionLevel is) will be
  22.  * in the logs, but they will contain all records, not only those above the level threshold.
  23.  *
  24.  * You can then have a passthruLevel as well which means that at the end of the request,
  25.  * even if it did not get activated, it will still send through log records of e.g. at least a
  26.  * warning level.
  27.  *
  28.  * You can find the various activation strategies in the
  29.  * Monolog\Handler\FingersCrossed\ namespace.
  30.  *
  31.  * @author Jordi Boggiano <j.boggiano@seld.be>
  32.  *
  33.  * @phpstan-import-type Record from \Monolog\Logger
  34.  * @phpstan-import-type Level from \Monolog\Logger
  35.  * @phpstan-import-type LevelName from \Monolog\Logger
  36.  */
  37. class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfaceResettableInterfaceFormattableHandlerInterface
  38. {
  39.     use ProcessableHandlerTrait;
  40.     /**
  41.      * @var callable|HandlerInterface
  42.      * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
  43.      */
  44.     protected $handler;
  45.     /** @var ActivationStrategyInterface */
  46.     protected $activationStrategy;
  47.     /** @var bool */
  48.     protected $buffering true;
  49.     /** @var int */
  50.     protected $bufferSize;
  51.     /** @var Record[] */
  52.     protected $buffer = [];
  53.     /** @var bool */
  54.     protected $stopBuffering;
  55.     /**
  56.      * @var ?int
  57.      * @phpstan-var ?Level
  58.      */
  59.     protected $passthruLevel;
  60.     /** @var bool */
  61.     protected $bubble;
  62.     /**
  63.      * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
  64.      *
  65.      * @param callable|HandlerInterface              $handler            Handler or factory callable($record|null, $fingersCrossedHandler).
  66.      * @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated
  67.      * @param int                                    $bufferSize         How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
  68.      * @param bool                                   $bubble             Whether the messages that are handled can bubble up the stack or not
  69.      * @param bool                                   $stopBuffering      Whether the handler should stop buffering after being triggered (default true)
  70.      * @param int|string                             $passthruLevel      Minimum level to always flush to handler on close, even if strategy not triggered
  71.      *
  72.      * @phpstan-param Level|LevelName|LogLevel::* $passthruLevel
  73.      * @phpstan-param Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy
  74.      */
  75.     public function __construct($handler$activationStrategy nullint $bufferSize 0bool $bubble truebool $stopBuffering true$passthruLevel null)
  76.     {
  77.         if (null === $activationStrategy) {
  78.             $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING);
  79.         }
  80.         // convert simple int activationStrategy to an object
  81.         if (!$activationStrategy instanceof ActivationStrategyInterface) {
  82.             $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy);
  83.         }
  84.         $this->handler $handler;
  85.         $this->activationStrategy $activationStrategy;
  86.         $this->bufferSize $bufferSize;
  87.         $this->bubble $bubble;
  88.         $this->stopBuffering $stopBuffering;
  89.         if ($passthruLevel !== null) {
  90.             $this->passthruLevel Logger::toMonologLevel($passthruLevel);
  91.         }
  92.         if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
  93.             throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
  94.         }
  95.     }
  96.     /**
  97.      * {@inheritDoc}
  98.      */
  99.     public function isHandling(array $record): bool
  100.     {
  101.         return true;
  102.     }
  103.     /**
  104.      * Manually activate this logger regardless of the activation strategy
  105.      */
  106.     public function activate(): void
  107.     {
  108.         if ($this->stopBuffering) {
  109.             $this->buffering false;
  110.         }
  111.         $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer);
  112.         $this->buffer = [];
  113.     }
  114.     /**
  115.      * {@inheritDoc}
  116.      */
  117.     public function handle(array $record): bool
  118.     {
  119.         if ($this->processors) {
  120.             /** @var Record $record */
  121.             $record $this->processRecord($record);
  122.         }
  123.         if ($this->buffering) {
  124.             $this->buffer[] = $record;
  125.             if ($this->bufferSize && count($this->buffer) > $this->bufferSize) {
  126.                 array_shift($this->buffer);
  127.             }
  128.             if ($this->activationStrategy->isHandlerActivated($record)) {
  129.                 $this->activate();
  130.             }
  131.         } else {
  132.             $this->getHandler($record)->handle($record);
  133.         }
  134.         return false === $this->bubble;
  135.     }
  136.     /**
  137.      * {@inheritDoc}
  138.      */
  139.     public function close(): void
  140.     {
  141.         $this->flushBuffer();
  142.         $this->getHandler()->close();
  143.     }
  144.     public function reset()
  145.     {
  146.         $this->flushBuffer();
  147.         $this->resetProcessors();
  148.         if ($this->getHandler() instanceof ResettableInterface) {
  149.             $this->getHandler()->reset();
  150.         }
  151.     }
  152.     /**
  153.      * Clears the buffer without flushing any messages down to the wrapped handler.
  154.      *
  155.      * It also resets the handler to its initial buffering state.
  156.      */
  157.     public function clear(): void
  158.     {
  159.         $this->buffer = [];
  160.         $this->reset();
  161.     }
  162.     /**
  163.      * Resets the state of the handler. Stops forwarding records to the wrapped handler.
  164.      */
  165.     private function flushBuffer(): void
  166.     {
  167.         if (null !== $this->passthruLevel) {
  168.             $level $this->passthruLevel;
  169.             $this->buffer array_filter($this->buffer, function ($record) use ($level) {
  170.                 return $record['level'] >= $level;
  171.             });
  172.             if (count($this->buffer) > 0) {
  173.                 $this->getHandler(end($this->buffer))->handleBatch($this->buffer);
  174.             }
  175.         }
  176.         $this->buffer = [];
  177.         $this->buffering true;
  178.     }
  179.     /**
  180.      * Return the nested handler
  181.      *
  182.      * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
  183.      *
  184.      * @return HandlerInterface
  185.      *
  186.      * @phpstan-param Record $record
  187.      */
  188.     public function getHandler(array $record null)
  189.     {
  190.         if (!$this->handler instanceof HandlerInterface) {
  191.             $this->handler = ($this->handler)($record$this);
  192.             if (!$this->handler instanceof HandlerInterface) {
  193.                 throw new \RuntimeException("The factory callable should return a HandlerInterface");
  194.             }
  195.         }
  196.         return $this->handler;
  197.     }
  198.     /**
  199.      * {@inheritDoc}
  200.      */
  201.     public function setFormatter(FormatterInterface $formatter): HandlerInterface
  202.     {
  203.         $handler $this->getHandler();
  204.         if ($handler instanceof FormattableHandlerInterface) {
  205.             $handler->setFormatter($formatter);
  206.             return $this;
  207.         }
  208.         throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.');
  209.     }
  210.     /**
  211.      * {@inheritDoc}
  212.      */
  213.     public function getFormatter(): FormatterInterface
  214.     {
  215.         $handler $this->getHandler();
  216.         if ($handler instanceof FormattableHandlerInterface) {
  217.             return $handler->getFormatter();
  218.         }
  219.         throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.');
  220.     }
  221. }