From 3e9c2ed046d0d7255fdc9e5060f99d56c04c10df Mon Sep 17 00:00:00 2001 From: Deeka Wong Date: Fri, 7 Feb 2025 10:45:40 +0800 Subject: [PATCH] feat: Add RedisCommandExecutedListener (#833) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add RedisCommandExecutedListener * fix: 更新事件处理逻辑以支持多种事件类型 * fix: 修复 CronEventListener 中的默认输出逻辑 * fix: 优化 RedisCommandExecutedListener 中的事件启用逻辑 --------- Co-authored-by: Deeka Wong <8337659+huangdijia@users.noreply.github.com> --- src/telescope/src/Aspect/RedisAspect.php | 4 +- src/telescope/src/ConfigProvider.php | 3 +- .../src/Listener/CommandListener.php | 7 +- .../src/Listener/CronEventListener.php | 6 +- .../src/Listener/DbQueryListener.php | 5 +- .../src/Listener/ExceptionHandlerListener.php | 20 +-- .../Listener/RedisCommandExecutedListener.php | 117 ++++++++++++++++++ 7 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 src/telescope/src/Listener/RedisCommandExecutedListener.php diff --git a/src/telescope/src/Aspect/RedisAspect.php b/src/telescope/src/Aspect/RedisAspect.php index 96431a5ab..9dc738cf6 100644 --- a/src/telescope/src/Aspect/RedisAspect.php +++ b/src/telescope/src/Aspect/RedisAspect.php @@ -28,6 +28,7 @@ use function Hyperf\Tappable\tap; /** + * @deprecated since v3.1, will remove in v3.2 * @property string $poolName * @property PackerInterface $packer */ @@ -49,7 +50,8 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) $startTime = microtime(true); return tap($proceedingJoinPoint->process(), function ($result) use ($proceedingJoinPoint, $startTime) { if ( - ! $this->telescopeConfig->isEnable('redis') + class_exists('Hyperf\Redis\Event\CommandExecuted') + || ! $this->telescopeConfig->isEnable('redis') || ! TelescopeContext::getBatchId() ) { return; diff --git a/src/telescope/src/ConfigProvider.php b/src/telescope/src/ConfigProvider.php index a27fad75d..50b9f98ee 100644 --- a/src/telescope/src/ConfigProvider.php +++ b/src/telescope/src/ConfigProvider.php @@ -41,11 +41,12 @@ public function __invoke(): array Contract\PrunableRepository::class => fn ($container) => $container->get(Contract\EntriesRepository::class), ], 'listeners' => [ - Listener\CronEventListener::class, Listener\CommandListener::class, + Listener\CronEventListener::class, Listener\DbQueryListener::class, Listener\ExceptionHandlerListener::class, Listener\FetchRecordingOnBootListener::class, + Listener\RedisCommandExecutedListener::class, Listener\RegisterRoutesListener::class => -1, ], 'publish' => [ diff --git a/src/telescope/src/Listener/CommandListener.php b/src/telescope/src/Listener/CommandListener.php index 9f7622941..8a06412ee 100644 --- a/src/telescope/src/Listener/CommandListener.php +++ b/src/telescope/src/Listener/CommandListener.php @@ -36,11 +36,14 @@ public function listen(): array } /** - * @param AfterExecute $event + * @param AfterExecute|object $event */ public function process(object $event): void { - if (! $this->telescopeConfig->isEnable('command')) { + if ( + ! $event instanceof AfterExecute + || ! $this->telescopeConfig->isEnable('command') + ) { return; } diff --git a/src/telescope/src/Listener/CronEventListener.php b/src/telescope/src/Listener/CronEventListener.php index 83f6e0eb5..ab6f76a14 100644 --- a/src/telescope/src/Listener/CronEventListener.php +++ b/src/telescope/src/Listener/CronEventListener.php @@ -38,7 +38,10 @@ public function listen(): array */ public function process(object $event): void { - if (! $this->telescopeConfig->isEnable('schedule')) { + if ( + ! ($event instanceof Event\AfterExecute || $event instanceof Event\FailToExecute) + || ! $this->telescopeConfig->isEnable('schedule') + ) { return; } @@ -47,7 +50,6 @@ public function process(object $event): void $output = match (true) { $event instanceof Event\AfterExecute => 'success', $event instanceof Event\FailToExecute => '[fail]' . (string) $event->getThrowable(), - default => '', }; Telescope::recordSchedule(IncomingEntry::make([ diff --git a/src/telescope/src/Listener/DbQueryListener.php b/src/telescope/src/Listener/DbQueryListener.php index 3603d25cb..3c6695d68 100644 --- a/src/telescope/src/Listener/DbQueryListener.php +++ b/src/telescope/src/Listener/DbQueryListener.php @@ -34,12 +34,13 @@ public function listen(): array } /** - * @param QueryExecuted $event + * @param object|QueryExecuted $event */ public function process(object $event): void { if ( - ! $this->telescopeConfig->isEnable('db') + ! $event instanceof QueryExecuted + || ! $this->telescopeConfig->isEnable('db') || ! TelescopeContext::getBatchId() ) { return; diff --git a/src/telescope/src/Listener/ExceptionHandlerListener.php b/src/telescope/src/Listener/ExceptionHandlerListener.php index 101ed2c3b..f0f8594e3 100644 --- a/src/telescope/src/Listener/ExceptionHandlerListener.php +++ b/src/telescope/src/Listener/ExceptionHandlerListener.php @@ -20,6 +20,7 @@ use Hyperf\Event\Contract\ListenerInterface; use Hyperf\HttpServer\Event; use Hyperf\Stringable\Str; +use Throwable; class ExceptionHandlerListener implements ListenerInterface { @@ -35,12 +36,13 @@ public function listen(): array } /** - * @param Event\RequestTerminated $event + * @param Event\RequestTerminated|object $event */ public function process(object $event): void { if ( - ! $this->telescopeConfig->isEnable('exception') + ! $event instanceof Event\RequestTerminated + || ! $this->telescopeConfig->isEnable('exception') || ! TelescopeContext::getBatchId() ) { return; @@ -50,9 +52,9 @@ public function process(object $event): void return; } - $trace = (new Collection($exception->getTrace()))->map(function ($item) { - return Arr::only($item, ['file', 'line']); - })->toArray(); + $trace = (new Collection($exception->getTrace())) + ->map(fn ($item) => Arr::only($item, ['file', 'line'])) + ->toArray(); Telescope::recordException(IncomingEntry::make([ 'class' => get_class($exception), @@ -65,6 +67,9 @@ public function process(object $event): void ])); } + /** + * @param Throwable $exception + */ protected function getContext($exception): array { if (Str::contains($exception->getFile(), "eval()'d code")) { @@ -75,8 +80,7 @@ protected function getContext($exception): array return (new Collection(explode("\n", file_get_contents($exception->getFile())))) ->slice($exception->getLine() - 10, 20) - ->mapWithKeys(function ($value, $key) { - return [$key + 1 => $value]; - })->all(); + ->mapWithKeys(fn ($value, $key) => [$key + 1 => $value]) + ->all(); } } diff --git a/src/telescope/src/Listener/RedisCommandExecutedListener.php b/src/telescope/src/Listener/RedisCommandExecutedListener.php new file mode 100644 index 000000000..8910fbae7 --- /dev/null +++ b/src/telescope/src/Listener/RedisCommandExecutedListener.php @@ -0,0 +1,117 @@ +telescopeConfig->isEnable('redis') && $this->setRedisEventEnable(); + } + + public function listen(): array + { + return [ + CommandExecuted::class, + ]; + } + + /** + * @param object|CommandExecuted $event + */ + public function process(object $event): void + { + if ( + ! $event instanceof CommandExecuted + || ! $this->telescopeConfig->isEnable('redis') + || ! TelescopeContext::getBatchId() + ) { + return; + } + + $command = $this->formatCommand($event->command, $event->parameters); + + if (str_contains($command, 'telescope')) { + return; + } + + Telescope::recordRedis(IncomingEntry::make([ + 'connection' => $event->connection, + 'command' => $command, + 'time' => number_format($event->time * 1000, 2, '.', ''), + ])); + } + + private function formatCommand(string $command, array $parameters): string + { + $parameters = (new Collection($parameters)) + ->map(function ($parameter, $key) use ($command) { + if (is_array($parameter)) { + return (new Collection($parameter)) + ->map(function ($value, $key) { + if (is_array($value)) { + return json_encode($value); + } + + return is_int($key) ? $value : "{$key} {$value}"; + }) + ->implode(' '); + } + if ( + $command == 'set' + && $key == 1 + && $driver = TelescopeContext::getCacheDriver() + ) { + $packerClass = $this->config->get('cache.' . $driver . '.packer', ''); + $packer = $this->container->has($packerClass) ? $this->container->get($packerClass) : null; + if ($packer && $packer instanceof PackerInterface) { + try { + $unpacked = $packer->unpack((string) $parameter); + $parameter = match (true) { + is_null($unpacked) => 'null', + is_array($unpacked) => json_encode($unpacked), + default => $unpacked, + }; + } catch (Throwable $e) { + } + } + } + + return $parameter; + }) + ->implode(' '); + + return "{$command} {$parameters}"; + } + + private function setRedisEventEnable() + { + foreach ((array) $this->config->get('redis', []) as $connection => $_) { + $this->config->set('redis.' . $connection . '.event.enable', true); + } + } +}