OverflowHandler.php 4.8 KB

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