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\Formatter\FormatterInterface; 15 16/** 17 * Sampling handler 18 * 19 * A sampled event stream can be useful for logging high frequency events in 20 * a production environment where you only need an idea of what is happening 21 * and are not concerned with capturing every occurrence. Since the decision to 22 * handle or not handle a particular event is determined randomly, the 23 * resulting sampled log is not guaranteed to contain 1/N of the events that 24 * occurred in the application, but based on the Law of large numbers, it will 25 * tend to be close to this ratio with a large number of attempts. 26 * 27 * @author Bryan Davis <bd808@wikimedia.org> 28 * @author Kunal Mehta <legoktm@gmail.com> 29 * 30 * @phpstan-import-type Record from \Monolog\Logger 31 * @phpstan-import-type Level from \Monolog\Logger 32 */ 33class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface 34{ 35 use ProcessableHandlerTrait; 36 37 /** 38 * @var HandlerInterface|callable 39 * @phpstan-var HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface 40 */ 41 protected $handler; 42 43 /** 44 * @var int $factor 45 */ 46 protected $factor; 47 48 /** 49 * @psalm-param HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface $handler 50 * 51 * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler). 52 * @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled) 53 */ 54 public function __construct($handler, int $factor) 55 { 56 parent::__construct(); 57 $this->handler = $handler; 58 $this->factor = $factor; 59 60 if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { 61 throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); 62 } 63 } 64 65 public function isHandling(array $record): bool 66 { 67 return $this->getHandler($record)->isHandling($record); 68 } 69 70 public function handle(array $record): bool 71 { 72 if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { 73 if ($this->processors) { 74 /** @var Record $record */ 75 $record = $this->processRecord($record); 76 } 77 78 $this->getHandler($record)->handle($record); 79 } 80 81 return false === $this->bubble; 82 } 83 84 /** 85 * Return the nested handler 86 * 87 * If the handler was provided as a factory callable, this will trigger the handler's instantiation. 88 * 89 * @phpstan-param Record|array{level: Level}|null $record 90 * 91 * @return HandlerInterface 92 */ 93 public function getHandler(array $record = null) 94 { 95 if (!$this->handler instanceof HandlerInterface) { 96 $this->handler = ($this->handler)($record, $this); 97 if (!$this->handler instanceof HandlerInterface) { 98 throw new \RuntimeException("The factory callable should return a HandlerInterface"); 99 } 100 } 101 102 return $this->handler; 103 } 104 105 /** 106 * {@inheritDoc} 107 */ 108 public function setFormatter(FormatterInterface $formatter): HandlerInterface 109 { 110 $handler = $this->getHandler(); 111 if ($handler instanceof FormattableHandlerInterface) { 112 $handler->setFormatter($formatter); 113 114 return $this; 115 } 116 117 throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); 118 } 119 120 /** 121 * {@inheritDoc} 122 */ 123 public function getFormatter(): FormatterInterface 124 { 125 $handler = $this->getHandler(); 126 if ($handler instanceof FormattableHandlerInterface) { 127 return $handler->getFormatter(); 128 } 129 130 throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); 131 } 132} 133