diff --git a/src/Monolog/Handler/RedisPubSubHandler.php b/src/Monolog/Handler/RedisPubSubHandler.php new file mode 100644 index 00000000..c9b13198 --- /dev/null +++ b/src/Monolog/Handler/RedisPubSubHandler.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; + +/** + * Sends the message to a Redis Pub/Sub channel using PUBLISH + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Logger::WARNING); + * $log->pushHandler($redis); + * + * @author Gaëtan Faugère + */ +class RedisPubSubHandler extends AbstractProcessingHandler +{ + private $redisClient; + private $channelKey; + + /** + * @param \Predis\Client|\Redis $redis The redis instance + * @param string $key The channel key to publish records to + * @param string|int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->channelKey = $key; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->redisClient->publish($this->channelKey, $record["formatted"]); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/tests/Monolog/Handler/RedisPubSubHandlerTest.php b/tests/Monolog/Handler/RedisPubSubHandlerTest.php new file mode 100644 index 00000000..5a37d410 --- /dev/null +++ b/tests/Monolog/Handler/RedisPubSubHandlerTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Test\TestCase; +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +class RedisPubSubHandlerTest extends TestCase +{ + public function testConstructorShouldThrowExceptionForInvalidRedis() + { + $this->expectException(\InvalidArgumentException::class); + + new RedisPubSubHandler(new \stdClass(), 'key'); + } + + public function testConstructorShouldWorkWithPredis() + { + $redis = $this->createMock('Predis\Client'); + $this->assertInstanceof('Monolog\Handler\RedisPubSubHandler', new RedisPubSubHandler($redis, 'key')); + } + + public function testConstructorShouldWorkWithRedis() + { + if (!class_exists('Redis')) { + $this->markTestSkipped('The redis ext is required to run this test'); + } + + $redis = $this->createMock('Redis'); + $this->assertInstanceof('Monolog\Handler\RedisPubSubHandler', new RedisPubSubHandler($redis, 'key')); + } + + public function testPredisHandle() + { + $redis = $this->prophesize('Predis\Client'); + $redis->publish('key', 'test')->shouldBeCalled(); + $redis = $redis->reveal(); + + $record = $this->getRecord(Logger::WARNING, 'test', ['data' => new \stdClass(), 'foo' => 34]); + + $handler = new RedisPubSubHandler($redis, 'key'); + $handler->setFormatter(new LineFormatter("%message%")); + $handler->handle($record); + } + + public function testRedisHandle() + { + if (!class_exists('Redis')) { + $this->markTestSkipped('The redis ext is required to run this test'); + } + + $redis = $this->createPartialMock('Redis', ['publish']); + + $redis->expects($this->once()) + ->method('publish') + ->with('key', 'test'); + + $record = $this->getRecord(Logger::WARNING, 'test', ['data' => new \stdClass(), 'foo' => 34]); + + $handler = new RedisPubSubHandler($redis, 'key'); + $handler->setFormatter(new LineFormatter("%message%")); + $handler->handle($record); + } +}