OverflowHandler.php 4.2 KB

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