1<?php declare(strict_types=1); 2 3/* 4 * This file is part of the Monolog package. 5 * 6 * (c) Jordi Boggiano <j.boggiano@seld.be> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Monolog\Handler; 13 14use Monolog\Logger; 15use Monolog\Formatter\FormatterInterface; 16 17/** 18 * Handler to only pass log messages when a certain threshold of number of messages is reached. 19 * 20 * This can be useful in cases of processing a batch of data, but you're for example only interested 21 * in case it fails catastrophically instead of a warning for 1 or 2 events. Worse things can happen, right? 22 * 23 * Usage example: 24 * 25 * ``` 26 * $log = new Logger('application'); 27 * $handler = new SomeHandler(...) 28 * 29 * // Pass all warnings to the handler when more than 10 & all error messages when more then 5 30 * $overflow = new OverflowHandler($handler, [Logger::WARNING => 10, Logger::ERROR => 5]); 31 * 32 * $log->pushHandler($overflow); 33 *``` 34 * 35 * @author Kris Buist <krisbuist@gmail.com> 36 */ 37class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface 38{ 39 /** @var HandlerInterface */ 40 private $handler; 41 42 /** @var int[] */ 43 private $thresholdMap = [ 44 Logger::DEBUG => 0, 45 Logger::INFO => 0, 46 Logger::NOTICE => 0, 47 Logger::WARNING => 0, 48 Logger::ERROR => 0, 49 Logger::CRITICAL => 0, 50 Logger::ALERT => 0, 51 Logger::EMERGENCY => 0, 52 ]; 53 54 /** 55 * Buffer of all messages passed to the handler before the threshold was reached 56 * 57 * @var mixed[][] 58 */ 59 private $buffer = []; 60 61 /** 62 * @param HandlerInterface $handler 63 * @param int[] $thresholdMap Dictionary of logger level => threshold 64 */ 65 public function __construct( 66 HandlerInterface $handler, 67 array $thresholdMap = [], 68 $level = Logger::DEBUG, 69 bool $bubble = true 70 ) { 71 $this->handler = $handler; 72 foreach ($thresholdMap as $thresholdLevel => $threshold) { 73 $this->thresholdMap[$thresholdLevel] = $threshold; 74 } 75 parent::__construct($level, $bubble); 76 } 77 78 /** 79 * Handles a record. 80 * 81 * All records may be passed to this method, and the handler should discard 82 * those that it does not want to handle. 83 * 84 * The return value of this function controls the bubbling process of the handler stack. 85 * Unless the bubbling is interrupted (by returning true), the Logger class will keep on 86 * calling further handlers in the stack with a given log record. 87 * 88 * {@inheritDoc} 89 */ 90 public function handle(array $record): bool 91 { 92 if ($record['level'] < $this->level) { 93 return false; 94 } 95 96 $level = $record['level']; 97 98 if (!isset($this->thresholdMap[$level])) { 99 $this->thresholdMap[$level] = 0; 100 } 101 102 if ($this->thresholdMap[$level] > 0) { 103 // The overflow threshold is not yet reached, so we're buffering the record and lowering the threshold by 1 104 $this->thresholdMap[$level]--; 105 $this->buffer[$level][] = $record; 106 107 return false === $this->bubble; 108 } 109 110 if ($this->thresholdMap[$level] == 0) { 111 // This current message is breaking the threshold. Flush the buffer and continue handling the current record 112 foreach ($this->buffer[$level] ?? [] as $buffered) { 113 $this->handler->handle($buffered); 114 } 115 $this->thresholdMap[$level]--; 116 unset($this->buffer[$level]); 117 } 118 119 $this->handler->handle($record); 120 121 return false === $this->bubble; 122 } 123 124 /** 125 * {@inheritDoc} 126 */ 127 public function setFormatter(FormatterInterface $formatter): HandlerInterface 128 { 129 if ($this->handler instanceof FormattableHandlerInterface) { 130 $this->handler->setFormatter($formatter); 131 132 return $this; 133 } 134 135 throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); 136 } 137 138 /** 139 * {@inheritDoc} 140 */ 141 public function getFormatter(): FormatterInterface 142 { 143 if ($this->handler instanceof FormattableHandlerInterface) { 144 return $this->handler->getFormatter(); 145 } 146 147 throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); 148 } 149} 150