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\LineFormatter; 15use Monolog\Formatter\FormatterInterface; 16use Monolog\Logger; 17 18/** 19 * Logs to a Redis key using rpush 20 * 21 * usage example: 22 * 23 * $log = new Logger('application'); 24 * $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod"); 25 * $log->pushHandler($redis); 26 * 27 * @author Thomas Tourlourat <thomas@tourlourat.com> 28 * 29 * @phpstan-import-type FormattedRecord from AbstractProcessingHandler 30 */ 31class RedisHandler extends AbstractProcessingHandler 32{ 33 /** @var \Predis\Client|\Redis */ 34 private $redisClient; 35 /** @var string */ 36 private $redisKey; 37 /** @var int */ 38 protected $capSize; 39 40 /** 41 * @param \Predis\Client|\Redis $redis The redis instance 42 * @param string $key The key name to push records to 43 * @param int $capSize Number of entries to limit list size to, 0 = unlimited 44 */ 45 public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true, int $capSize = 0) 46 { 47 if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { 48 throw new \InvalidArgumentException('Predis\Client or Redis instance required'); 49 } 50 51 $this->redisClient = $redis; 52 $this->redisKey = $key; 53 $this->capSize = $capSize; 54 55 parent::__construct($level, $bubble); 56 } 57 58 /** 59 * {@inheritDoc} 60 */ 61 protected function write(array $record): void 62 { 63 if ($this->capSize) { 64 $this->writeCapped($record); 65 } else { 66 $this->redisClient->rpush($this->redisKey, $record["formatted"]); 67 } 68 } 69 70 /** 71 * Write and cap the collection 72 * Writes the record to the redis list and caps its 73 * 74 * @phpstan-param FormattedRecord $record 75 */ 76 protected function writeCapped(array $record): void 77 { 78 if ($this->redisClient instanceof \Redis) { 79 $mode = defined('\Redis::MULTI') ? \Redis::MULTI : 1; 80 $this->redisClient->multi($mode) 81 ->rpush($this->redisKey, $record["formatted"]) 82 ->ltrim($this->redisKey, -$this->capSize, -1) 83 ->exec(); 84 } else { 85 $redisKey = $this->redisKey; 86 $capSize = $this->capSize; 87 $this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) { 88 $tx->rpush($redisKey, $record["formatted"]); 89 $tx->ltrim($redisKey, -$capSize, -1); 90 }); 91 } 92 } 93 94 /** 95 * {@inheritDoc} 96 */ 97 protected function getDefaultFormatter(): FormatterInterface 98 { 99 return new LineFormatter(); 100 } 101} 102