From 23f2a155ea9e128d119f93dd94a4bcde5224dfbf Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 07:20:49 +0800 Subject: [PATCH 01/23] feat: add additional Sentry singleton classes to aspect configuration Add support for new Sentry singleton classes: - LogLevel::getInstance - Metrics::getInstance - MetricsUnit::getInstance - Unit::getInstance This ensures proper aspect-based handling for these Sentry components. --- src/sentry/src/Aspect/SingletonAspect.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sentry/src/Aspect/SingletonAspect.php b/src/sentry/src/Aspect/SingletonAspect.php index bcd559415..eed9fad68 100644 --- a/src/sentry/src/Aspect/SingletonAspect.php +++ b/src/sentry/src/Aspect/SingletonAspect.php @@ -22,6 +22,9 @@ class SingletonAspect extends AbstractAspect \Sentry\EventType::class . '::getInstance', \Sentry\MonitorScheduleUnit::class . '::getInstance', \Sentry\Integration\IntegrationRegistry::class . '::getInstance', + \Sentry\Logs\LogLevel::class . '::getInstance', + \Sentry\Metrics\Metrics::class . '::getInstance', + \Sentry\Unit::class . '::getInstance', \Sentry\State\HubAdapter::class . '::getInstance', \Sentry\Tracing\SpanStatus::class . '::getInstance', \Sentry\Tracing\TransactionSource::class . '::getInstance', From 83dfd96babe1cfdee8901234881297003882ab6f Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 07:21:45 +0800 Subject: [PATCH 02/23] fix: reorder Sentry singleton classes in SingletonAspect --- src/sentry/src/Aspect/SingletonAspect.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/src/Aspect/SingletonAspect.php b/src/sentry/src/Aspect/SingletonAspect.php index eed9fad68..12312e820 100644 --- a/src/sentry/src/Aspect/SingletonAspect.php +++ b/src/sentry/src/Aspect/SingletonAspect.php @@ -24,11 +24,11 @@ class SingletonAspect extends AbstractAspect \Sentry\Integration\IntegrationRegistry::class . '::getInstance', \Sentry\Logs\LogLevel::class . '::getInstance', \Sentry\Metrics\Metrics::class . '::getInstance', - \Sentry\Unit::class . '::getInstance', \Sentry\State\HubAdapter::class . '::getInstance', \Sentry\Tracing\SpanStatus::class . '::getInstance', \Sentry\Tracing\TransactionSource::class . '::getInstance', \Sentry\Transport\ResultStatus::class . '::getInstance', + \Sentry\Unit::class . '::getInstance', ]; public function process(ProceedingJoinPoint $proceedingJoinPoint) From f18ffc75709a8eb69af442b5baf8d6d54e6c8242 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 08:13:26 +0800 Subject: [PATCH 03/23] fix: update SingletonAspect to use TraceMetrics singleton instance --- src/sentry/src/Aspect/SingletonAspect.php | 2 +- src/sentry/src/Integration.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sentry/src/Aspect/SingletonAspect.php b/src/sentry/src/Aspect/SingletonAspect.php index 12312e820..5c0aec6e4 100644 --- a/src/sentry/src/Aspect/SingletonAspect.php +++ b/src/sentry/src/Aspect/SingletonAspect.php @@ -23,7 +23,7 @@ class SingletonAspect extends AbstractAspect \Sentry\MonitorScheduleUnit::class . '::getInstance', \Sentry\Integration\IntegrationRegistry::class . '::getInstance', \Sentry\Logs\LogLevel::class . '::getInstance', - \Sentry\Metrics\Metrics::class . '::getInstance', + \Sentry\Metrics\TraceMetrics::class . '::getInstance', \Sentry\State\HubAdapter::class . '::getInstance', \Sentry\Tracing\SpanStatus::class . '::getInstance', \Sentry\Tracing\TransactionSource::class . '::getInstance', diff --git a/src/sentry/src/Integration.php b/src/sentry/src/Integration.php index 7d934ba68..e26f0e80e 100644 --- a/src/sentry/src/Integration.php +++ b/src/sentry/src/Integration.php @@ -15,6 +15,7 @@ use Sentry\Event; use Sentry\Integration\IntegrationInterface; use Sentry\Logs\Logs; +use Sentry\Metrics\TraceMetrics; use Sentry\SentrySdk; use Sentry\State\Scope; use Sentry\Tracing\Span; @@ -111,6 +112,7 @@ public static function flushEvents(): void $client->flush(); Logs::getInstance()->flush(); + TraceMetrics::getInstance()->flush(); } } From 14c9808bd61793015db3fe92c27f4645b6177626 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 08:16:12 +0800 Subject: [PATCH 04/23] feat: add enable_metrics configuration option to Sentry settings --- src/sentry/publish/sentry.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sentry/publish/sentry.php b/src/sentry/publish/sentry.php index b29c88d38..c1ccfdad3 100644 --- a/src/sentry/publish/sentry.php +++ b/src/sentry/publish/sentry.php @@ -40,6 +40,9 @@ // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#enable_logs 'enable_logs' => env('SENTRY_ENABLE_LOGS', true), + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#enable_metrics + 'enable_metrics' => env('SENTRY_ENABLE_METRICS', true), + 'logs_channel_level' => env('SENTRY_LOGS_CHANNEL_LEVEL', Sentry\Logs\LogLevel::debug()), // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#send_default_pii From 6fd4f5751d08ec0fa6902ddd384a13461d130abf Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:01:50 +0800 Subject: [PATCH 05/23] feat: add metrics tracking and related listeners for enhanced performance monitoring --- src/sentry/src/Constants.php | 2 + src/sentry/src/Feature.php | 5 + .../src/Metrics/CoroutineServerStats.php | 56 ++++++++ .../src/Metrics/Event/MetricFactoryReady.php | 19 +++ .../src/Metrics/Listener/DBPoolWatcher.php | 38 +++++ .../src/Metrics/Listener/OnBeforeHandle.php | 41 ++++++ .../Listener/OnCoroutineServerStart.php | 120 ++++++++++++++++ .../Metrics/Listener/OnMetricFactoryReady.php | 133 ++++++++++++++++++ .../src/Metrics/Listener/OnWorkerStart.php | 125 ++++++++++++++++ .../src/Metrics/Listener/PoolWatcher.php | 97 +++++++++++++ .../src/Metrics/Listener/QueueWatcher.php | 93 ++++++++++++ .../src/Metrics/Listener/RedisPoolWatcher.php | 37 +++++ .../src/Metrics/Listener/RequestWatcher.php | 57 ++++++++ .../src/Metrics/Traits/MetricSetter.php | 33 +++++ 14 files changed, 856 insertions(+) create mode 100644 src/sentry/src/Metrics/CoroutineServerStats.php create mode 100644 src/sentry/src/Metrics/Event/MetricFactoryReady.php create mode 100644 src/sentry/src/Metrics/Listener/DBPoolWatcher.php create mode 100644 src/sentry/src/Metrics/Listener/OnBeforeHandle.php create mode 100644 src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php create mode 100644 src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php create mode 100644 src/sentry/src/Metrics/Listener/OnWorkerStart.php create mode 100644 src/sentry/src/Metrics/Listener/PoolWatcher.php create mode 100644 src/sentry/src/Metrics/Listener/QueueWatcher.php create mode 100644 src/sentry/src/Metrics/Listener/RedisPoolWatcher.php create mode 100644 src/sentry/src/Metrics/Listener/RequestWatcher.php create mode 100644 src/sentry/src/Metrics/Traits/MetricSetter.php diff --git a/src/sentry/src/Constants.php b/src/sentry/src/Constants.php index 6540efdc4..33b344c37 100644 --- a/src/sentry/src/Constants.php +++ b/src/sentry/src/Constants.php @@ -38,4 +38,6 @@ class Constants public const BAGGAGE = 'baggage'; public const TRACEPARENT = 'traceparent'; + + public const RUN_IN_COMMAND = 'sentry.run_in_command'; } diff --git a/src/sentry/src/Feature.php b/src/sentry/src/Feature.php index 6f052b672..3929be9d1 100644 --- a/src/sentry/src/Feature.php +++ b/src/sentry/src/Feature.php @@ -31,6 +31,11 @@ public function isBreadcrumbEnabled(string $key, bool $default = true): bool return (bool) $this->config->get('sentry.breadcrumbs.' . $key, $default); } + public function isMetricsEnabled(bool $default = true): bool + { + return (bool) $this->config->get('sentry.enable_metrics', $default); + } + public function isTracingEnabled(string $key, bool $default = true): bool { return (bool) $this->config->get('sentry.tracing.' . $key, $default); diff --git a/src/sentry/src/Metrics/CoroutineServerStats.php b/src/sentry/src/Metrics/CoroutineServerStats.php new file mode 100644 index 000000000..d150366f7 --- /dev/null +++ b/src/sentry/src/Metrics/CoroutineServerStats.php @@ -0,0 +1,56 @@ + 1, + 'idle_worker_num' => 0, + ]; + + public function __get($name) + { + return $this->stats[$name] ?? 0; + } + + public function __set($name, $value) + { + $this->stats[$name] = $value; + } + + public function toArray(): array + { + return $this->stats; + } +} diff --git a/src/sentry/src/Metrics/Event/MetricFactoryReady.php b/src/sentry/src/Metrics/Event/MetricFactoryReady.php new file mode 100644 index 000000000..bffe095d0 --- /dev/null +++ b/src/sentry/src/Metrics/Event/MetricFactoryReady.php @@ -0,0 +1,19 @@ +container->get(ConfigInterface::class); + $poolNames = array_keys($config->get('databases', ['default' => []])); + + foreach ($poolNames as $poolName) { + $workerId = (int) ($event->workerId ?? 0); + $pool = $this + ->container + ->get(PoolFactory::class) + ->getPool($poolName); + $this->watch($pool, $poolName, $workerId); + } + } +} diff --git a/src/sentry/src/Metrics/Listener/OnBeforeHandle.php b/src/sentry/src/Metrics/Listener/OnBeforeHandle.php new file mode 100644 index 000000000..b4954ee83 --- /dev/null +++ b/src/sentry/src/Metrics/Listener/OnBeforeHandle.php @@ -0,0 +1,41 @@ + Context::set(Constants::RUN_IN_COMMAND, true), + $event instanceof AfterExecute => Context::destroy(Constants::RUN_IN_COMMAND), + default => null, + }; + } +} diff --git a/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php new file mode 100644 index 000000000..8d2224efb --- /dev/null +++ b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php @@ -0,0 +1,120 @@ +timer = new Timer(); + } + + /** + * @return string[] returns the events that you want to listen + */ + public function listen(): array + { + return [ + MainCoroutineServerStart::class, + ]; + } + + public function process(object $event): void + { + if (! $this->feature->isMetricsEnabled()) { + return; + } + + if ($this->running) { + return; + } + + $this->running = true; + + $eventDispatcher = $this->container->get(EventDispatcherInterface::class); + $eventDispatcher->dispatch(new MetricFactoryReady()); + + // The following metrics MUST be collected in worker. + $metrics = [ + // 'worker_request_count', + // 'worker_dispatch_count', + 'memory_usage', + 'memory_peak_usage', + 'gc_runs', + 'gc_collected', + 'gc_threshold', + 'gc_roots', + 'ru_oublock', + 'ru_inblock', + 'ru_msgsnd', + 'ru_msgrcv', + 'ru_maxrss', + 'ru_ixrss', + 'ru_idrss', + 'ru_minflt', + 'ru_majflt', + 'ru_nsignals', + 'ru_nvcsw', + 'ru_nivcsw', + 'ru_nswap', + 'ru_utime_tv_usec', + 'ru_utime_tv_sec', + 'ru_stime_tv_usec', + 'ru_stime_tv_sec', + ]; + + $timerId = $this->timer->tick(1, function () use ($metrics) { + $this->trySet('gc_', $metrics, gc_status(), 0, Unit::second()); + $this->trySet('', $metrics, getrusage(), 0, Unit::second()); + + TraceMetrics::getInstance()->gauge( + 'memory_usage', + (float) memory_get_usage(), + [], + Unit::second() + ); + TraceMetrics::getInstance()->gauge( + 'memory_peak_usage', + (float) memory_get_peak_usage(), + [], + Unit::second() + ); + }); + // Clean up timer on worker exit; + Coroutine::create(function () use ($timerId) { + CoordinatorManager::until(Constants::WORKER_EXIT)->yield(); + $this->timer->clear($timerId); + }); + } +} diff --git a/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php b/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php new file mode 100644 index 000000000..4c1334b6e --- /dev/null +++ b/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php @@ -0,0 +1,133 @@ +timer = new Timer(); + } + + public function listen(): array + { + return [ + MetricFactoryReady::class, + ]; + } + + public function process(object $event): void + { + if (! $this->feature->isMetricsEnabled()) { + return; + } + + $metrics = [ + 'sys_load', + 'event_num', + 'signal_listener_num', + 'aio_task_num', + 'aio_worker_num', + 'c_stack_size', + 'coroutine_num', + 'coroutine_peak_num', + 'coroutine_last_cid', + 'connection_num', + 'accept_count', + 'close_count', + 'worker_num', + 'idle_worker_num', + 'tasking_num', + 'request_count', + 'timer_num', + 'timer_round', + 'swoole_timer_num', + 'swoole_timer_round', + 'metric_process_memory_usage', + 'metric_process_memory_peak_usage', + ]; + + $serverStatsFactory = null; + + if (! Context::get(\FriendsOfHyperf\Sentry\Constants::RUN_IN_COMMAND, false)) { + if ($this->container->has(SwooleServer::class) && $server = $this->container->get(SwooleServer::class)) { + if ($server instanceof SwooleServer) { + $serverStatsFactory = fn (): array => $server->stats(); + } + } + + if (! $serverStatsFactory) { + $serverStatsFactory = fn (): array => $this->container->get(CoroutineServerStats::class)->toArray(); + } + } + + $timerId = $this->timer->tick(1, function () use ($metrics, $serverStatsFactory) { + $this->trySet('', $metrics, Coroutine::stats(), 0, Unit::second()); + $this->trySet('timer_', $metrics, Timer::stats(), 0, Unit::second()); + + if ($serverStatsFactory) { + $this->trySet('', $metrics, $serverStatsFactory(), 0, Unit::second()); + } + + if (class_exists('Swoole\Timer')) { + $this->trySet('swoole_timer_', $metrics, \Swoole\Timer::stats(), 0, Unit::second()); + } + + $load = sys_getloadavg(); + TraceMetrics::getInstance()->gauge( + 'sys_load', + round($load[0] / System::getCpuCoresNum(), 2), + ['worker' => '0'], + Unit::second() + ); + TraceMetrics::getInstance()->gauge( + 'metric_process_memory_usage', + (float) memory_get_usage(), + ['worker' => '0'], + Unit::second() + ); + TraceMetrics::getInstance()->gauge( + 'metric_process_memory_peak_usage', + (float) memory_get_peak_usage(), + ['worker' => '0'], + Unit::second() + ); + }); + + Coroutine::create(function () use ($timerId) { + CoordinatorManager::until(Constants::WORKER_EXIT)->yield(); + $this->timer->clear($timerId); + }); + } +} diff --git a/src/sentry/src/Metrics/Listener/OnWorkerStart.php b/src/sentry/src/Metrics/Listener/OnWorkerStart.php new file mode 100644 index 000000000..f75f5fd27 --- /dev/null +++ b/src/sentry/src/Metrics/Listener/OnWorkerStart.php @@ -0,0 +1,125 @@ +timer = new Timer(); + } + + public function listen(): array + { + return [ + BeforeWorkerStart::class, + ]; + } + + public function process(object $event): void + { + if (! $this->feature->isMetricsEnabled()) { + return; + } + + $workerId = $event->workerId; + $eventDispatcher = $this->container->get(EventDispatcherInterface::class); + $eventDispatcher->dispatch(new MetricFactoryReady($workerId)); + + // The following metrics MUST be collected in worker. + $metrics = [ + 'worker_request_count', + 'worker_dispatch_count', + 'memory_usage', + 'memory_peak_usage', + 'gc_runs', + 'gc_collected', + 'gc_threshold', + 'gc_roots', + 'ru_oublock', + 'ru_inblock', + 'ru_msgsnd', + 'ru_msgrcv', + 'ru_maxrss', + 'ru_ixrss', + 'ru_idrss', + 'ru_minflt', + 'ru_majflt', + 'ru_nsignals', + 'ru_nvcsw', + 'ru_nivcsw', + 'ru_nswap', + 'ru_utime_tv_usec', + 'ru_utime_tv_sec', + 'ru_stime_tv_usec', + 'ru_stime_tv_sec', + ]; + + $timerId = $this->timer->tick(1, function () use ($metrics, $event) { + $server = $this->container->get(Server::class); + $serverStats = $server->stats(); + $this->trySet('gc_', $metrics, gc_status()); + $this->trySet('', $metrics, getrusage()); + + TraceMetrics::getInstance()->gauge( + 'worker_request_count', + (float) $serverStats['worker_request_count'], + ['worker' => (string) ($event->workerId ?? 0)], + Unit::second() + ); + TraceMetrics::getInstance()->gauge( + 'worker_dispatch_count', + (float) $serverStats['worker_dispatch_count'], + ['worker' => (string) ($event->workerId ?? 0)], + Unit::second() + ); + TraceMetrics::getInstance()->gauge( + 'memory_usage', + (float) memory_get_usage(), + ['worker' => (string) ($event->workerId ?? 0)], + Unit::second() + ); + TraceMetrics::getInstance()->gauge( + 'memory_peak_usage', + (float) memory_get_peak_usage(), + ['worker' => (string) ($event->workerId ?? 0)], + Unit::second() + ); + }); + + Coroutine::create(function () use ($timerId) { + CoordinatorManager::until(Constants::WORKER_EXIT)->yield(); + $this->timer->clear($timerId); + }); + } +} diff --git a/src/sentry/src/Metrics/Listener/PoolWatcher.php b/src/sentry/src/Metrics/Listener/PoolWatcher.php new file mode 100644 index 000000000..8b90554a4 --- /dev/null +++ b/src/sentry/src/Metrics/Listener/PoolWatcher.php @@ -0,0 +1,97 @@ +timer = new Timer(); + } + + /** + * @return string[] returns the events that you want to listen + */ + public function listen(): array + { + return [ + BeforeWorkerStart::class, + MainCoroutineServerStart::class, + ]; + } + + abstract public function getPrefix(): string; + + abstract public function process(object $event): void; + + public function watch(Pool $pool, string $poolName, int $workerId) + { + if (! $this->feature->isMetricsEnabled()) { + return; + } + + $timerId = $this->timer->tick(1, function () use ( + $pool, + $workerId, + $poolName + ) { + TraceMetrics::getInstance()->gauge( + $this->getPrefix() . '_connections_in_use', + (float) $pool->getCurrentConnections(), + [ + 'pool' => $poolName, + 'worker' => (string) $workerId, + ], + Unit::second() + ); + TraceMetrics::getInstance()->gauge( + $this->getPrefix() . '_connections_in_waiting', + (float) $pool->getConnectionsInChannel(), + [ + 'pool' => $poolName, + 'worker' => (string) $workerId, + ], + Unit::second() + ); + TraceMetrics::getInstance()->gauge( + $this->getPrefix() . '_max_connections', + (float) $pool->getOption()->getMaxConnections(), + [ + 'pool' => $poolName, + 'worker' => (string) $workerId, + ], + Unit::second() + ); + }); + Coroutine::create(function () use ($timerId) { + CoordinatorManager::until(Constants::WORKER_EXIT)->yield(); + $this->timer->clear($timerId); + }); + } +} diff --git a/src/sentry/src/Metrics/Listener/QueueWatcher.php b/src/sentry/src/Metrics/Listener/QueueWatcher.php new file mode 100644 index 000000000..e4d119c5f --- /dev/null +++ b/src/sentry/src/Metrics/Listener/QueueWatcher.php @@ -0,0 +1,93 @@ +timer = new Timer(); + } + + /** + * @return string[] returns the events that you want to listen + */ + public function listen(): array + { + return [ + MetricFactoryReady::class, + ]; + } + + /** + * Periodically scan metrics. + */ + public function process(object $event): void + { + if (! $this->feature->isMetricsEnabled()) { + return; + } + + $timerId = $this->timer->tick(1, function () { + $config = $this->container->get(ConfigInterface::class); + foreach ($config->get('async_queue', []) as $name => $options) { + $queue = $this->container->get(DriverFactory::class)->get($name); + $info = $queue->info(); + + TraceMetrics::getInstance()->gauge( + 'queue_waiting', + (float) $info['waiting'], + ['queue' => $name], + \Sentry\Unit::second() + ); + TraceMetrics::getInstance()->gauge( + 'queue_delayed', + (float) $info['delayed'], + ['queue' => $name], + \Sentry\Unit::second() + ); + TraceMetrics::getInstance()->gauge( + 'queue_failed', + (float) $info['failed'], + ['queue' => $name], + \Sentry\Unit::second() + ); + TraceMetrics::getInstance()->gauge( + 'queue_timeout', + (float) $info['timeout'], + ['queue' => $name], + \Sentry\Unit::second() + ); + } + }); + Coroutine::create(function () use ($timerId) { + CoordinatorManager::until(Constants::WORKER_EXIT)->yield(); + $this->timer->clear($timerId); + }); + } +} diff --git a/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php b/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php new file mode 100644 index 000000000..cc163d398 --- /dev/null +++ b/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php @@ -0,0 +1,37 @@ +container->get(ConfigInterface::class); + $poolNames = array_keys($config->get('redis', ['default' => []])); + foreach ($poolNames as $poolName) { + $workerId = (int) ($event->workerId ?? 0); + $pool = $this + ->container + ->get(PoolFactory::class) + ->getPool($poolName); + $this->watch($pool, $poolName, $workerId); + } + } +} diff --git a/src/sentry/src/Metrics/Listener/RequestWatcher.php b/src/sentry/src/Metrics/Listener/RequestWatcher.php new file mode 100644 index 000000000..620945651 --- /dev/null +++ b/src/sentry/src/Metrics/Listener/RequestWatcher.php @@ -0,0 +1,57 @@ + $this->handleRequestReceived(), + $event instanceof HttpEvent\RequestHandled, $event instanceof RpcEvent\RequestHandled => $this->handleRequestHandled(), + default => null, + }; + } + + protected function handleRequestReceived(): void + { + ++$this->stats->accept_count; + ++$this->stats->request_count; + ++$this->stats->connection_num; + } + + protected function handleRequestHandled(): void + { + ++$this->stats->close_count; + ++$this->stats->response_count; + --$this->stats->connection_num; + } +} diff --git a/src/sentry/src/Metrics/Traits/MetricSetter.php b/src/sentry/src/Metrics/Traits/MetricSetter.php new file mode 100644 index 000000000..ef839740b --- /dev/null +++ b/src/sentry/src/Metrics/Traits/MetricSetter.php @@ -0,0 +1,33 @@ +gauge( + $metricsKey, + (float) $stats[$key], + ['worker' => (string) $workerId], + $unit + ); + } + } + } +} From 255a74fe67ac394cbc1654437ae4b4b3d2f5c3dc Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:03:50 +0800 Subject: [PATCH 06/23] feat: add new metrics listeners for enhanced monitoring capabilities --- src/sentry/src/ConfigProvider.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sentry/src/ConfigProvider.php b/src/sentry/src/ConfigProvider.php index c2b1caf19..13b261d71 100644 --- a/src/sentry/src/ConfigProvider.php +++ b/src/sentry/src/ConfigProvider.php @@ -60,6 +60,15 @@ public function __invoke(): array Listener\SetupSentryListener::class, Listener\EventHandleListener::class => PHP_INT_MAX - 1, Crons\Listener\EventHandleListener::class => PHP_INT_MAX - 1, + Metrics\Listener\DBPoolWatcher::class, + Metrics\Listener\OnBeforeHandle::class, + Metrics\Listener\OnCoroutineServerStart::class, + Metrics\Listener\OnMetricFactoryReady::class, + Metrics\Listener\OnWorkerStart::class, + Metrics\Listener\PoolWatcher::class, + Metrics\Listener\QueueWatcher::class, + Metrics\Listener\RedisPoolWatcher::class, + Metrics\Listener\RequestWatcher::class, Tracing\Listener\EventHandleListener::class => PHP_INT_MAX, // !! Make sure it is the first one to handle the event ], 'annotations' => [ From 94cd380863c75304a7b532fbe73ddd3955a1fdfc Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:13:43 +0800 Subject: [PATCH 07/23] fix: remove PoolWatcher listener from ConfigProvider --- src/sentry/src/ConfigProvider.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sentry/src/ConfigProvider.php b/src/sentry/src/ConfigProvider.php index 13b261d71..7149827b6 100644 --- a/src/sentry/src/ConfigProvider.php +++ b/src/sentry/src/ConfigProvider.php @@ -65,7 +65,6 @@ public function __invoke(): array Metrics\Listener\OnCoroutineServerStart::class, Metrics\Listener\OnMetricFactoryReady::class, Metrics\Listener\OnWorkerStart::class, - Metrics\Listener\PoolWatcher::class, Metrics\Listener\QueueWatcher::class, Metrics\Listener\RedisPoolWatcher::class, Metrics\Listener\RequestWatcher::class, From 52a049ff31ca2d04922288a3cdda32b03de719e7 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:20:18 +0800 Subject: [PATCH 08/23] feat: update event parameter types in metrics listeners for improved clarity --- .../src/Metrics/Listener/DBPoolWatcher.php | 5 +++++ .../Listener/OnCoroutineServerStart.php | 6 +++--- .../Metrics/Listener/OnMetricFactoryReady.php | 20 +++++++++++-------- .../src/Metrics/Listener/OnWorkerStart.php | 3 +++ .../src/Metrics/Listener/PoolWatcher.php | 3 +++ .../src/Metrics/Listener/QueueWatcher.php | 3 ++- .../src/Metrics/Listener/RedisPoolWatcher.php | 5 +++++ 7 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/sentry/src/Metrics/Listener/DBPoolWatcher.php b/src/sentry/src/Metrics/Listener/DBPoolWatcher.php index 3fc5cfaf2..da9cacb7b 100644 --- a/src/sentry/src/Metrics/Listener/DBPoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/DBPoolWatcher.php @@ -13,6 +13,8 @@ use Hyperf\Contract\ConfigInterface; use Hyperf\DbConnection\Pool\PoolFactory; +use Hyperf\Framework\Event\BeforeWorkerStart; +use Hyperf\Server\Event\MainCoroutineServerStart; class DBPoolWatcher extends PoolWatcher { @@ -21,6 +23,9 @@ public function getPrefix(): string return 'mysql'; } + /** + * @param object|BeforeWorkerStart|MainCoroutineServerStart $event + */ public function process(object $event): void { $config = $this->container->get(ConfigInterface::class); diff --git a/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php index 8d2224efb..5614f57c1 100644 --- a/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php +++ b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php @@ -40,9 +40,6 @@ public function __construct( $this->timer = new Timer(); } - /** - * @return string[] returns the events that you want to listen - */ public function listen(): array { return [ @@ -50,6 +47,9 @@ public function listen(): array ]; } + /** + * @param object|MainCoroutineServerStart $event + */ public function process(object $event): void { if (! $this->feature->isMetricsEnabled()) { diff --git a/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php b/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php index 4c1334b6e..68f432883 100644 --- a/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php +++ b/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php @@ -47,12 +47,16 @@ public function listen(): array ]; } + /** + * @param object|MetricFactoryReady $event + */ public function process(object $event): void { if (! $this->feature->isMetricsEnabled()) { return; } + $workerId = $event->workerId; $metrics = [ 'sys_load', 'event_num', @@ -92,35 +96,35 @@ public function process(object $event): void } } - $timerId = $this->timer->tick(1, function () use ($metrics, $serverStatsFactory) { - $this->trySet('', $metrics, Coroutine::stats(), 0, Unit::second()); - $this->trySet('timer_', $metrics, Timer::stats(), 0, Unit::second()); + $timerId = $this->timer->tick(1, function () use ($metrics, $serverStatsFactory, $workerId) { + $this->trySet('', $metrics, Coroutine::stats(), $workerId, Unit::second()); + $this->trySet('timer_', $metrics, Timer::stats(), $workerId, Unit::second()); if ($serverStatsFactory) { - $this->trySet('', $metrics, $serverStatsFactory(), 0, Unit::second()); + $this->trySet('', $metrics, $serverStatsFactory(), $workerId, Unit::second()); } if (class_exists('Swoole\Timer')) { - $this->trySet('swoole_timer_', $metrics, \Swoole\Timer::stats(), 0, Unit::second()); + $this->trySet('swoole_timer_', $metrics, \Swoole\Timer::stats(), $workerId, Unit::second()); } $load = sys_getloadavg(); TraceMetrics::getInstance()->gauge( 'sys_load', round($load[0] / System::getCpuCoresNum(), 2), - ['worker' => '0'], + ['worker' => (string) $workerId], Unit::second() ); TraceMetrics::getInstance()->gauge( 'metric_process_memory_usage', (float) memory_get_usage(), - ['worker' => '0'], + ['worker' => (string) $workerId], Unit::second() ); TraceMetrics::getInstance()->gauge( 'metric_process_memory_peak_usage', (float) memory_get_peak_usage(), - ['worker' => '0'], + ['worker' => (string) $workerId], Unit::second() ); }); diff --git a/src/sentry/src/Metrics/Listener/OnWorkerStart.php b/src/sentry/src/Metrics/Listener/OnWorkerStart.php index f75f5fd27..82de0e487 100644 --- a/src/sentry/src/Metrics/Listener/OnWorkerStart.php +++ b/src/sentry/src/Metrics/Listener/OnWorkerStart.php @@ -46,6 +46,9 @@ public function listen(): array ]; } + /** + * @param object|BeforeWorkerStart $event + */ public function process(object $event): void { if (! $this->feature->isMetricsEnabled()) { diff --git a/src/sentry/src/Metrics/Listener/PoolWatcher.php b/src/sentry/src/Metrics/Listener/PoolWatcher.php index 8b90554a4..5e99ef947 100644 --- a/src/sentry/src/Metrics/Listener/PoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/PoolWatcher.php @@ -48,6 +48,9 @@ public function listen(): array abstract public function getPrefix(): string; + /** + * @param object|BeforeWorkerStart|MainCoroutineServerStart $event + */ abstract public function process(object $event): void; public function watch(Pool $pool, string $poolName, int $workerId) diff --git a/src/sentry/src/Metrics/Listener/QueueWatcher.php b/src/sentry/src/Metrics/Listener/QueueWatcher.php index e4d119c5f..cf03afe51 100644 --- a/src/sentry/src/Metrics/Listener/QueueWatcher.php +++ b/src/sentry/src/Metrics/Listener/QueueWatcher.php @@ -45,7 +45,7 @@ public function listen(): array } /** - * Periodically scan metrics. + * @param object|MetricFactoryReady $event */ public function process(object $event): void { @@ -85,6 +85,7 @@ public function process(object $event): void ); } }); + Coroutine::create(function () use ($timerId) { CoordinatorManager::until(Constants::WORKER_EXIT)->yield(); $this->timer->clear($timerId); diff --git a/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php b/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php index cc163d398..19d2fe40d 100644 --- a/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php @@ -12,7 +12,9 @@ namespace FriendsOfHyperf\Sentry\Metrics\Listener; use Hyperf\Contract\ConfigInterface; +use Hyperf\Framework\Event\BeforeWorkerStart; use Hyperf\Redis\Pool\PoolFactory; +use Hyperf\Server\Event\MainCoroutineServerStart; class RedisPoolWatcher extends PoolWatcher { @@ -21,6 +23,9 @@ public function getPrefix(): string return 'redis'; } + /** + * @param object|BeforeWorkerStart|MainCoroutineServerStart $event + */ public function process(object $event): void { $config = $this->container->get(ConfigInterface::class); From c263c7681377e9535ae44a72923ece3d8d8314ab Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:21:23 +0800 Subject: [PATCH 09/23] fix: optimize queue retrieval in QueueWatcher listener --- src/sentry/src/Metrics/Listener/QueueWatcher.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sentry/src/Metrics/Listener/QueueWatcher.php b/src/sentry/src/Metrics/Listener/QueueWatcher.php index cf03afe51..ff60b9cdb 100644 --- a/src/sentry/src/Metrics/Listener/QueueWatcher.php +++ b/src/sentry/src/Metrics/Listener/QueueWatcher.php @@ -55,7 +55,9 @@ public function process(object $event): void $timerId = $this->timer->tick(1, function () { $config = $this->container->get(ConfigInterface::class); - foreach ($config->get('async_queue', []) as $name => $options) { + $queues = array_keys($config->get('async_queue', [])); + + foreach ($queues as $name) { $queue = $this->container->get(DriverFactory::class)->get($name); $info = $queue->info(); From 7d273b6d9ca9c523959b1d453486d4df79c5d432 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:41:22 +0800 Subject: [PATCH 10/23] feat: add metrics interval configuration and update timer intervals in listeners --- src/sentry/src/Feature.php | 5 +++++ src/sentry/src/Metrics/Listener/DBPoolWatcher.php | 2 +- .../Metrics/Listener/OnCoroutineServerStart.php | 10 +++++----- .../src/Metrics/Listener/OnMetricFactoryReady.php | 15 +++++++-------- src/sentry/src/Metrics/Listener/OnWorkerStart.php | 8 +++----- src/sentry/src/Metrics/Listener/PoolWatcher.php | 12 ++++-------- src/sentry/src/Metrics/Listener/QueueWatcher.php | 14 +++++--------- 7 files changed, 30 insertions(+), 36 deletions(-) diff --git a/src/sentry/src/Feature.php b/src/sentry/src/Feature.php index 3929be9d1..339f2c623 100644 --- a/src/sentry/src/Feature.php +++ b/src/sentry/src/Feature.php @@ -36,6 +36,11 @@ public function isMetricsEnabled(bool $default = true): bool return (bool) $this->config->get('sentry.enable_metrics', $default); } + public function getMetricsInterval(int $default = 10): int + { + return (int) $this->config->get('sentry.metrics_interval', $default); + } + public function isTracingEnabled(string $key, bool $default = true): bool { return (bool) $this->config->get('sentry.tracing.' . $key, $default); diff --git a/src/sentry/src/Metrics/Listener/DBPoolWatcher.php b/src/sentry/src/Metrics/Listener/DBPoolWatcher.php index da9cacb7b..63a890b75 100644 --- a/src/sentry/src/Metrics/Listener/DBPoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/DBPoolWatcher.php @@ -20,7 +20,7 @@ class DBPoolWatcher extends PoolWatcher { public function getPrefix(): string { - return 'mysql'; + return 'db'; } /** diff --git a/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php index 5614f57c1..a815ba061 100644 --- a/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php +++ b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php @@ -94,21 +94,21 @@ public function process(object $event): void 'ru_stime_tv_sec', ]; - $timerId = $this->timer->tick(1, function () use ($metrics) { - $this->trySet('gc_', $metrics, gc_status(), 0, Unit::second()); - $this->trySet('', $metrics, getrusage(), 0, Unit::second()); + $timerId = $this->timer->tick($this->feature->getMetricsInterval(), function () use ($metrics) { + $this->trySet('gc_', $metrics, gc_status()); + $this->trySet('', $metrics, getrusage()); TraceMetrics::getInstance()->gauge( 'memory_usage', (float) memory_get_usage(), [], - Unit::second() + Unit::byte() ); TraceMetrics::getInstance()->gauge( 'memory_peak_usage', (float) memory_get_peak_usage(), [], - Unit::second() + Unit::byte() ); }); // Clean up timer on worker exit; diff --git a/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php b/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php index 68f432883..36f1d953c 100644 --- a/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php +++ b/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php @@ -96,16 +96,16 @@ public function process(object $event): void } } - $timerId = $this->timer->tick(1, function () use ($metrics, $serverStatsFactory, $workerId) { - $this->trySet('', $metrics, Coroutine::stats(), $workerId, Unit::second()); - $this->trySet('timer_', $metrics, Timer::stats(), $workerId, Unit::second()); + $timerId = $this->timer->tick($this->feature->getMetricsInterval(), function () use ($metrics, $serverStatsFactory, $workerId) { + $this->trySet('', $metrics, Coroutine::stats(), $workerId); + $this->trySet('timer_', $metrics, Timer::stats(), $workerId); if ($serverStatsFactory) { - $this->trySet('', $metrics, $serverStatsFactory(), $workerId, Unit::second()); + $this->trySet('', $metrics, $serverStatsFactory(), $workerId); } if (class_exists('Swoole\Timer')) { - $this->trySet('swoole_timer_', $metrics, \Swoole\Timer::stats(), $workerId, Unit::second()); + $this->trySet('swoole_timer_', $metrics, \Swoole\Timer::stats(), $workerId); } $load = sys_getloadavg(); @@ -113,19 +113,18 @@ public function process(object $event): void 'sys_load', round($load[0] / System::getCpuCoresNum(), 2), ['worker' => (string) $workerId], - Unit::second() ); TraceMetrics::getInstance()->gauge( 'metric_process_memory_usage', (float) memory_get_usage(), ['worker' => (string) $workerId], - Unit::second() + Unit::byte() ); TraceMetrics::getInstance()->gauge( 'metric_process_memory_peak_usage', (float) memory_get_peak_usage(), ['worker' => (string) $workerId], - Unit::second() + Unit::byte() ); }); diff --git a/src/sentry/src/Metrics/Listener/OnWorkerStart.php b/src/sentry/src/Metrics/Listener/OnWorkerStart.php index 82de0e487..8f1b8bcef 100644 --- a/src/sentry/src/Metrics/Listener/OnWorkerStart.php +++ b/src/sentry/src/Metrics/Listener/OnWorkerStart.php @@ -88,7 +88,7 @@ public function process(object $event): void 'ru_stime_tv_sec', ]; - $timerId = $this->timer->tick(1, function () use ($metrics, $event) { + $timerId = $this->timer->tick($this->feature->getMetricsInterval(), function () use ($metrics, $event) { $server = $this->container->get(Server::class); $serverStats = $server->stats(); $this->trySet('gc_', $metrics, gc_status()); @@ -98,25 +98,23 @@ public function process(object $event): void 'worker_request_count', (float) $serverStats['worker_request_count'], ['worker' => (string) ($event->workerId ?? 0)], - Unit::second() ); TraceMetrics::getInstance()->gauge( 'worker_dispatch_count', (float) $serverStats['worker_dispatch_count'], ['worker' => (string) ($event->workerId ?? 0)], - Unit::second() ); TraceMetrics::getInstance()->gauge( 'memory_usage', (float) memory_get_usage(), ['worker' => (string) ($event->workerId ?? 0)], - Unit::second() + Unit::byte() ); TraceMetrics::getInstance()->gauge( 'memory_peak_usage', (float) memory_get_peak_usage(), ['worker' => (string) ($event->workerId ?? 0)], - Unit::second() + Unit::byte() ); }); diff --git a/src/sentry/src/Metrics/Listener/PoolWatcher.php b/src/sentry/src/Metrics/Listener/PoolWatcher.php index 5e99ef947..07392086b 100644 --- a/src/sentry/src/Metrics/Listener/PoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/PoolWatcher.php @@ -22,7 +22,6 @@ use Hyperf\Server\Event\MainCoroutineServerStart; use Psr\Container\ContainerInterface; use Sentry\Metrics\TraceMetrics; -use Sentry\Unit; abstract class PoolWatcher implements ListenerInterface { @@ -59,7 +58,7 @@ public function watch(Pool $pool, string $poolName, int $workerId) return; } - $timerId = $this->timer->tick(1, function () use ( + $timerId = $this->timer->tick($this->feature->getMetricsInterval(), function () use ( $pool, $workerId, $poolName @@ -70,8 +69,7 @@ public function watch(Pool $pool, string $poolName, int $workerId) [ 'pool' => $poolName, 'worker' => (string) $workerId, - ], - Unit::second() + ] ); TraceMetrics::getInstance()->gauge( $this->getPrefix() . '_connections_in_waiting', @@ -79,8 +77,7 @@ public function watch(Pool $pool, string $poolName, int $workerId) [ 'pool' => $poolName, 'worker' => (string) $workerId, - ], - Unit::second() + ] ); TraceMetrics::getInstance()->gauge( $this->getPrefix() . '_max_connections', @@ -88,8 +85,7 @@ public function watch(Pool $pool, string $poolName, int $workerId) [ 'pool' => $poolName, 'worker' => (string) $workerId, - ], - Unit::second() + ] ); }); Coroutine::create(function () use ($timerId) { diff --git a/src/sentry/src/Metrics/Listener/QueueWatcher.php b/src/sentry/src/Metrics/Listener/QueueWatcher.php index ff60b9cdb..67747da72 100644 --- a/src/sentry/src/Metrics/Listener/QueueWatcher.php +++ b/src/sentry/src/Metrics/Listener/QueueWatcher.php @@ -53,7 +53,7 @@ public function process(object $event): void return; } - $timerId = $this->timer->tick(1, function () { + $timerId = $this->timer->tick($this->feature->getMetricsInterval(), function () { $config = $this->container->get(ConfigInterface::class); $queues = array_keys($config->get('async_queue', [])); @@ -64,26 +64,22 @@ public function process(object $event): void TraceMetrics::getInstance()->gauge( 'queue_waiting', (float) $info['waiting'], - ['queue' => $name], - \Sentry\Unit::second() + ['queue' => $name] ); TraceMetrics::getInstance()->gauge( 'queue_delayed', (float) $info['delayed'], - ['queue' => $name], - \Sentry\Unit::second() + ['queue' => $name] ); TraceMetrics::getInstance()->gauge( 'queue_failed', (float) $info['failed'], - ['queue' => $name], - \Sentry\Unit::second() + ['queue' => $name] ); TraceMetrics::getInstance()->gauge( 'queue_timeout', (float) $info['timeout'], - ['queue' => $name], - \Sentry\Unit::second() + ['queue' => $name] ); } }); From 082920d5da1f117c91d59c0c3da5b411eff0425f Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:42:03 +0800 Subject: [PATCH 11/23] feat: add metrics interval configuration to Sentry settings --- src/sentry/publish/sentry.php | 1 + src/sentry/src/Factory/ClientBuilderFactory.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/sentry/publish/sentry.php b/src/sentry/publish/sentry.php index c1ccfdad3..dd3187711 100644 --- a/src/sentry/publish/sentry.php +++ b/src/sentry/publish/sentry.php @@ -42,6 +42,7 @@ // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#enable_metrics 'enable_metrics' => env('SENTRY_ENABLE_METRICS', true), + 'metrics_interval' => (int) env('SENTRY_METRICS_INTERVAL', 10), 'logs_channel_level' => env('SENTRY_LOGS_CHANNEL_LEVEL', Sentry\Logs\LogLevel::debug()), diff --git a/src/sentry/src/Factory/ClientBuilderFactory.php b/src/sentry/src/Factory/ClientBuilderFactory.php index aaa970ab1..ce478b03b 100644 --- a/src/sentry/src/Factory/ClientBuilderFactory.php +++ b/src/sentry/src/Factory/ClientBuilderFactory.php @@ -31,6 +31,7 @@ class ClientBuilderFactory 'http_chanel_size', // deprecated, will be removed in v3.2 'http_concurrent_limit', // deprecated, will be removed in v3.2 'logs_channel_level', + 'metrics_interval', 'transport_channel_size', 'transport_concurrent_limit', 'tracing', From 65a56ef7c35489a0d79d1bba8c612c924fce2a9b Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:45:50 +0800 Subject: [PATCH 12/23] feat: remove unnecessary comment and improve PoolWatcher with metric name prefix method --- src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php | 2 +- src/sentry/src/Metrics/Listener/PoolWatcher.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php index a815ba061..52d7fe271 100644 --- a/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php +++ b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php @@ -111,7 +111,7 @@ public function process(object $event): void Unit::byte() ); }); - // Clean up timer on worker exit; + Coroutine::create(function () use ($timerId) { CoordinatorManager::until(Constants::WORKER_EXIT)->yield(); $this->timer->clear($timerId); diff --git a/src/sentry/src/Metrics/Listener/PoolWatcher.php b/src/sentry/src/Metrics/Listener/PoolWatcher.php index 07392086b..50cc373ba 100644 --- a/src/sentry/src/Metrics/Listener/PoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/PoolWatcher.php @@ -45,6 +45,11 @@ public function listen(): array ]; } + /** + * Get the metric name prefix for this pool type (e.g., 'redis', 'mysql'). + * + * @return string The prefix used in metric names like '{prefix}_connections_in_use' + */ abstract public function getPrefix(): string; /** From a8cff6f638485a27cf43718dce96ef5181720daf Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:46:13 +0800 Subject: [PATCH 13/23] fix: add return type declaration to watch method in PoolWatcher class --- src/sentry/src/Metrics/Listener/PoolWatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/src/Metrics/Listener/PoolWatcher.php b/src/sentry/src/Metrics/Listener/PoolWatcher.php index 50cc373ba..ddf592bf1 100644 --- a/src/sentry/src/Metrics/Listener/PoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/PoolWatcher.php @@ -57,7 +57,7 @@ abstract public function getPrefix(): string; */ abstract public function process(object $event): void; - public function watch(Pool $pool, string $poolName, int $workerId) + public function watch(Pool $pool, string $poolName, int $workerId): void { if (! $this->feature->isMetricsEnabled()) { return; From 60f7e90d58b77b926bcb3d8072370569582637ed Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:46:55 +0800 Subject: [PATCH 14/23] fix: add missing newline for better code readability in PoolWatcher class --- src/sentry/src/Metrics/Listener/PoolWatcher.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/src/Metrics/Listener/PoolWatcher.php b/src/sentry/src/Metrics/Listener/PoolWatcher.php index ddf592bf1..b31ab753f 100644 --- a/src/sentry/src/Metrics/Listener/PoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/PoolWatcher.php @@ -93,6 +93,7 @@ public function watch(Pool $pool, string $poolName, int $workerId): void ] ); }); + Coroutine::create(function () use ($timerId) { CoordinatorManager::until(Constants::WORKER_EXIT)->yield(); $this->timer->clear($timerId); From e4e6d1e9a6a769760db7706a57b1db8d24685c45 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:50:52 +0800 Subject: [PATCH 15/23] feat: enforce minimum metrics interval of 5 in getMetricsInterval method --- src/sentry/src/Feature.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sentry/src/Feature.php b/src/sentry/src/Feature.php index 339f2c623..6b0169af5 100644 --- a/src/sentry/src/Feature.php +++ b/src/sentry/src/Feature.php @@ -38,7 +38,13 @@ public function isMetricsEnabled(bool $default = true): bool public function getMetricsInterval(int $default = 10): int { - return (int) $this->config->get('sentry.metrics_interval', $default); + $interval = (int) $this->config->get('sentry.metrics_interval', $default); + + if ($interval < 5) { + return 5; + } + + return $interval; } public function isTracingEnabled(string $key, bool $default = true): bool From cdf795bc2883b9ad1a4e745a2fbb953f47f8b1de Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:54:11 +0800 Subject: [PATCH 16/23] refactor: simplify process method in RequestWatcher class by removing match expression --- .../src/Metrics/Listener/RequestWatcher.php | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/sentry/src/Metrics/Listener/RequestWatcher.php b/src/sentry/src/Metrics/Listener/RequestWatcher.php index 620945651..31e508ab0 100644 --- a/src/sentry/src/Metrics/Listener/RequestWatcher.php +++ b/src/sentry/src/Metrics/Listener/RequestWatcher.php @@ -12,6 +12,7 @@ namespace FriendsOfHyperf\Sentry\Metrics\Listener; use FriendsOfHyperf\Sentry\Metrics\CoroutineServerStats; +use Hyperf\Engine\Coroutine; use Hyperf\Event\Contract\ListenerInterface; use Hyperf\HttpServer\Event as HttpEvent; use Hyperf\RpcServer\Event as RpcEvent; @@ -34,24 +35,16 @@ public function listen(): array public function process(object $event): void { - match (true) { - $event instanceof HttpEvent\RequestReceived, $event instanceof RpcEvent\RequestReceived => $this->handleRequestReceived(), - $event instanceof HttpEvent\RequestHandled, $event instanceof RpcEvent\RequestHandled => $this->handleRequestHandled(), - default => null, - }; - } - - protected function handleRequestReceived(): void - { - ++$this->stats->accept_count; - ++$this->stats->request_count; - ++$this->stats->connection_num; - } - - protected function handleRequestHandled(): void - { - ++$this->stats->close_count; - ++$this->stats->response_count; - --$this->stats->connection_num; + if ($event instanceof HttpEvent\RequestReceived || $event instanceof RpcEvent\RequestReceived) { + ++$this->stats->accept_count; + ++$this->stats->request_count; + ++$this->stats->connection_num; + + Coroutine::defer(function () { + ++$this->stats->close_count; + ++$this->stats->response_count; + --$this->stats->connection_num; + }); + } } } From bb3a2df61d2787aa1c705ac2c3b60b61a4be1a34 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:57:44 +0800 Subject: [PATCH 17/23] fix: add missing newline for improved readability in RedisPoolWatcher class --- src/sentry/src/Metrics/Listener/RedisPoolWatcher.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php b/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php index 19d2fe40d..0fbe5c154 100644 --- a/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/RedisPoolWatcher.php @@ -30,6 +30,7 @@ public function process(object $event): void { $config = $this->container->get(ConfigInterface::class); $poolNames = array_keys($config->get('redis', ['default' => []])); + foreach ($poolNames as $poolName) { $workerId = (int) ($event->workerId ?? 0); $pool = $this From 87c9180cfea6f5f0683aa96acb3fd65d1eea493a Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:12:48 +0800 Subject: [PATCH 18/23] feat: add Counter and CounterAspect classes for metrics annotation and processing --- src/sentry/src/Metrics/Annotation/Counter.php | 23 +++++++ .../src/Metrics/Aspect/CounterAspect.php | 66 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 src/sentry/src/Metrics/Annotation/Counter.php create mode 100644 src/sentry/src/Metrics/Aspect/CounterAspect.php diff --git a/src/sentry/src/Metrics/Annotation/Counter.php b/src/sentry/src/Metrics/Annotation/Counter.php new file mode 100644 index 000000000..87db5687f --- /dev/null +++ b/src/sentry/src/Metrics/Annotation/Counter.php @@ -0,0 +1,23 @@ +feature->isMetricsEnabled()) { + $metadata = $proceedingJoinPoint->getAnnotationMetadata(); + $source = $this->fromCamelCase($proceedingJoinPoint->className . '::' . $proceedingJoinPoint->methodName); + + /** @var null|Counter $annotation */ + $annotation = $metadata->method[Counter::class] ?? null; + + if ($annotation) { + $name = $annotation->name ?: $source; + } else { + $name = $source; + } + + TraceMetrics::getInstance() + ->count($name, 1, [ + 'class' => $proceedingJoinPoint->className, + 'method' => $proceedingJoinPoint->methodName, + ]); + } + + return $proceedingJoinPoint->process(); + } + + private function fromCamelCase(string $input): string + { + preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches); + $ret = $matches[0]; + foreach ($ret as &$match) { + $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match); + } + return implode('_', $ret); + } +} From 95e01d89d9b0c69ce86c416c94a7d976aa5a5aef Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:14:44 +0800 Subject: [PATCH 19/23] feat: add CounterAspect to ConfigProvider for metrics tracking --- src/sentry/src/ConfigProvider.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/src/ConfigProvider.php b/src/sentry/src/ConfigProvider.php index 7149827b6..fafa26229 100644 --- a/src/sentry/src/ConfigProvider.php +++ b/src/sentry/src/ConfigProvider.php @@ -27,6 +27,7 @@ public function __invoke(): array Aspect\LoggerAspect::class, Aspect\RedisAspect::class, Aspect\SingletonAspect::class, + Metrics\Aspect\CounterAspect::class, Tracing\Aspect\AmqpProducerAspect::class, Tracing\Aspect\AsyncQueueJobMessageAspect::class, Tracing\Aspect\CacheAspect::class, From 70ff61e1a91123965d01d14983fbb677a415dc06 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:21:29 +0800 Subject: [PATCH 20/23] feat: add HistogramAspect and Histogram annotation for enhanced metrics tracking --- src/sentry/src/ConfigProvider.php | 1 + src/sentry/src/Metrics/Annotation/Counter.php | 2 +- .../src/Metrics/Annotation/Histogram.php | 23 ++++++ .../src/Metrics/Aspect/HistogramAspect.php | 73 +++++++++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/sentry/src/Metrics/Annotation/Histogram.php create mode 100644 src/sentry/src/Metrics/Aspect/HistogramAspect.php diff --git a/src/sentry/src/ConfigProvider.php b/src/sentry/src/ConfigProvider.php index fafa26229..31a99d4fd 100644 --- a/src/sentry/src/ConfigProvider.php +++ b/src/sentry/src/ConfigProvider.php @@ -28,6 +28,7 @@ public function __invoke(): array Aspect\RedisAspect::class, Aspect\SingletonAspect::class, Metrics\Aspect\CounterAspect::class, + Metrics\Aspect\HistogramAspect::class, Tracing\Aspect\AmqpProducerAspect::class, Tracing\Aspect\AsyncQueueJobMessageAspect::class, Tracing\Aspect\CacheAspect::class, diff --git a/src/sentry/src/Metrics/Annotation/Counter.php b/src/sentry/src/Metrics/Annotation/Counter.php index 87db5687f..b4cc6aaf5 100644 --- a/src/sentry/src/Metrics/Annotation/Counter.php +++ b/src/sentry/src/Metrics/Annotation/Counter.php @@ -17,7 +17,7 @@ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] class Counter extends AbstractAnnotation { - public function __construct(public ?string $name = null) + public function __construct(public string $name = '') { } } diff --git a/src/sentry/src/Metrics/Annotation/Histogram.php b/src/sentry/src/Metrics/Annotation/Histogram.php new file mode 100644 index 000000000..49952921b --- /dev/null +++ b/src/sentry/src/Metrics/Annotation/Histogram.php @@ -0,0 +1,23 @@ +feature->isMetricsEnabled()) { + return $proceedingJoinPoint->process(); + } + + $metadata = $proceedingJoinPoint->getAnnotationMetadata(); + $source = $this->fromCamelCase($proceedingJoinPoint->className . '::' . $proceedingJoinPoint->methodName); + /** @var null|Histogram $annotation */ + $annotation = $metadata->method[Histogram::class] ?? null; + if ($annotation) { + $name = $annotation->name ?: $source; + } else { + $name = $source; + } + + $startAt = microtime(true); + + return tap($proceedingJoinPoint->process(), function () use ($name, $proceedingJoinPoint, $startAt) { + TraceMetrics::getInstance()->distribution( + $name, + (microtime(true) - $startAt) * 1000, + [ + 'class' => $proceedingJoinPoint->className, + 'method' => $proceedingJoinPoint->methodName, + ] + ); + }); + } + + private function fromCamelCase(string $input): string + { + preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches); + $ret = $matches[0]; + foreach ($ret as &$match) { + $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match); + } + return implode('_', $ret); + } +} From 0e3eeb4f93a90670f3580be97bbe4bfd34e7a6db Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:22:06 +0800 Subject: [PATCH 21/23] feat: add Unit::second() to HistogramAspect for improved metrics tracking --- src/sentry/src/Metrics/Aspect/HistogramAspect.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sentry/src/Metrics/Aspect/HistogramAspect.php b/src/sentry/src/Metrics/Aspect/HistogramAspect.php index 01bf27912..17dfb9c05 100644 --- a/src/sentry/src/Metrics/Aspect/HistogramAspect.php +++ b/src/sentry/src/Metrics/Aspect/HistogramAspect.php @@ -16,6 +16,7 @@ use Hyperf\Di\Aop\AbstractAspect; use Hyperf\Di\Aop\ProceedingJoinPoint; use Sentry\Metrics\TraceMetrics; +use Sentry\Unit; use function Hyperf\Tappable\tap; @@ -56,7 +57,8 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint): mixed [ 'class' => $proceedingJoinPoint->className, 'method' => $proceedingJoinPoint->methodName, - ] + ], + Unit::second() ); }); } From 58d646810ece6cfd9a1ac6647f6c18679409395f Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:26:47 +0800 Subject: [PATCH 22/23] feat: enhance RequestWatcher to track HTTP request metrics with TraceMetrics --- .../src/Metrics/Listener/RequestWatcher.php | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/sentry/src/Metrics/Listener/RequestWatcher.php b/src/sentry/src/Metrics/Listener/RequestWatcher.php index 31e508ab0..d807d08c0 100644 --- a/src/sentry/src/Metrics/Listener/RequestWatcher.php +++ b/src/sentry/src/Metrics/Listener/RequestWatcher.php @@ -15,7 +15,10 @@ use Hyperf\Engine\Coroutine; use Hyperf\Event\Contract\ListenerInterface; use Hyperf\HttpServer\Event as HttpEvent; +use Hyperf\HttpServer\Router\Dispatched; use Hyperf\RpcServer\Event as RpcEvent; +use Psr\Http\Message\ServerRequestInterface; +use Sentry\Metrics\TraceMetrics; class RequestWatcher implements ListenerInterface { @@ -40,11 +43,35 @@ public function process(object $event): void ++$this->stats->request_count; ++$this->stats->connection_num; - Coroutine::defer(function () { + $request = $event->request; + $startAt = microtime(true); + + Coroutine::defer(function () use ($request, $startAt) { ++$this->stats->close_count; ++$this->stats->response_count; --$this->stats->connection_num; + + TraceMetrics::getInstance()->distribution( + 'http_requests', + microtime(true) - $startAt, + [ + 'request_path' => $this->getPath($request), + 'request_method' => $request->getMethod(), + ] + ); }); } } + + protected function getPath(ServerRequestInterface $request): string + { + $dispatched = $request->getAttribute(Dispatched::class); + if (! $dispatched) { + return $request->getUri()->getPath(); + } + if (! $dispatched->handler) { + return 'not_found'; + } + return $dispatched->handler->route; + } } From 9e62103a6e149c3c5659409a9a3ce9c642bd74d6 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:33:59 +0800 Subject: [PATCH 23/23] feat: add defer calls to flush TraceMetrics in various aspects and listeners --- src/sentry/src/Metrics/Aspect/CounterAspect.php | 4 ++++ src/sentry/src/Metrics/Aspect/HistogramAspect.php | 3 +++ src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php | 4 ++++ src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php | 4 ++++ src/sentry/src/Metrics/Listener/OnWorkerStart.php | 4 ++++ src/sentry/src/Metrics/Listener/PoolWatcher.php | 4 ++++ src/sentry/src/Metrics/Listener/QueueWatcher.php | 4 ++++ src/sentry/src/Metrics/Listener/RequestWatcher.php | 2 ++ 8 files changed, 29 insertions(+) diff --git a/src/sentry/src/Metrics/Aspect/CounterAspect.php b/src/sentry/src/Metrics/Aspect/CounterAspect.php index 2705eca35..a312e1f06 100644 --- a/src/sentry/src/Metrics/Aspect/CounterAspect.php +++ b/src/sentry/src/Metrics/Aspect/CounterAspect.php @@ -17,6 +17,8 @@ use Hyperf\Di\Aop\ProceedingJoinPoint; use Sentry\Metrics\TraceMetrics; +use function Hyperf\Coroutine\defer; + class CounterAspect extends AbstractAspect { public array $classes = []; @@ -44,6 +46,8 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint): mixed $name = $source; } + defer(fn () => TraceMetrics::getInstance()->flush()); + TraceMetrics::getInstance() ->count($name, 1, [ 'class' => $proceedingJoinPoint->className, diff --git a/src/sentry/src/Metrics/Aspect/HistogramAspect.php b/src/sentry/src/Metrics/Aspect/HistogramAspect.php index 17dfb9c05..5ea1cb2f1 100644 --- a/src/sentry/src/Metrics/Aspect/HistogramAspect.php +++ b/src/sentry/src/Metrics/Aspect/HistogramAspect.php @@ -18,6 +18,7 @@ use Sentry\Metrics\TraceMetrics; use Sentry\Unit; +use function Hyperf\Coroutine\defer; use function Hyperf\Tappable\tap; class HistogramAspect extends AbstractAspect @@ -51,6 +52,8 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint): mixed $startAt = microtime(true); return tap($proceedingJoinPoint->process(), function () use ($name, $proceedingJoinPoint, $startAt) { + defer(fn () => TraceMetrics::getInstance()->flush()); + TraceMetrics::getInstance()->distribution( $name, (microtime(true) - $startAt) * 1000, diff --git a/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php index 52d7fe271..109ca8ea0 100644 --- a/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php +++ b/src/sentry/src/Metrics/Listener/OnCoroutineServerStart.php @@ -25,6 +25,8 @@ use Sentry\Metrics\TraceMetrics; use Sentry\Unit; +use function Hyperf\Coroutine\defer; + class OnCoroutineServerStart implements ListenerInterface { use MetricSetter; @@ -95,6 +97,8 @@ public function process(object $event): void ]; $timerId = $this->timer->tick($this->feature->getMetricsInterval(), function () use ($metrics) { + defer(fn () => TraceMetrics::getInstance()->flush()); + $this->trySet('gc_', $metrics, gc_status()); $this->trySet('', $metrics, getrusage()); diff --git a/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php b/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php index 36f1d953c..37eb6652f 100644 --- a/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php +++ b/src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php @@ -27,6 +27,8 @@ use Sentry\Unit; use Swoole\Server as SwooleServer; +use function Hyperf\Coroutine\defer; + class OnMetricFactoryReady implements ListenerInterface { use MetricSetter; @@ -97,6 +99,8 @@ public function process(object $event): void } $timerId = $this->timer->tick($this->feature->getMetricsInterval(), function () use ($metrics, $serverStatsFactory, $workerId) { + defer(fn () => TraceMetrics::getInstance()->flush()); + $this->trySet('', $metrics, Coroutine::stats(), $workerId); $this->trySet('timer_', $metrics, Timer::stats(), $workerId); diff --git a/src/sentry/src/Metrics/Listener/OnWorkerStart.php b/src/sentry/src/Metrics/Listener/OnWorkerStart.php index 8f1b8bcef..770ef9b8c 100644 --- a/src/sentry/src/Metrics/Listener/OnWorkerStart.php +++ b/src/sentry/src/Metrics/Listener/OnWorkerStart.php @@ -26,6 +26,8 @@ use Sentry\Unit; use Swoole\Server; +use function Hyperf\Coroutine\defer; + class OnWorkerStart implements ListenerInterface { use MetricSetter; @@ -89,6 +91,8 @@ public function process(object $event): void ]; $timerId = $this->timer->tick($this->feature->getMetricsInterval(), function () use ($metrics, $event) { + defer(fn () => TraceMetrics::getInstance()->flush()); + $server = $this->container->get(Server::class); $serverStats = $server->stats(); $this->trySet('gc_', $metrics, gc_status()); diff --git a/src/sentry/src/Metrics/Listener/PoolWatcher.php b/src/sentry/src/Metrics/Listener/PoolWatcher.php index b31ab753f..789c42815 100644 --- a/src/sentry/src/Metrics/Listener/PoolWatcher.php +++ b/src/sentry/src/Metrics/Listener/PoolWatcher.php @@ -23,6 +23,8 @@ use Psr\Container\ContainerInterface; use Sentry\Metrics\TraceMetrics; +use function Hyperf\Coroutine\defer; + abstract class PoolWatcher implements ListenerInterface { protected Timer $timer; @@ -68,6 +70,8 @@ public function watch(Pool $pool, string $poolName, int $workerId): void $workerId, $poolName ) { + defer(fn () => TraceMetrics::getInstance()->flush()); + TraceMetrics::getInstance()->gauge( $this->getPrefix() . '_connections_in_use', (float) $pool->getCurrentConnections(), diff --git a/src/sentry/src/Metrics/Listener/QueueWatcher.php b/src/sentry/src/Metrics/Listener/QueueWatcher.php index 67747da72..22ad95609 100644 --- a/src/sentry/src/Metrics/Listener/QueueWatcher.php +++ b/src/sentry/src/Metrics/Listener/QueueWatcher.php @@ -23,6 +23,8 @@ use Psr\Container\ContainerInterface; use Sentry\Metrics\TraceMetrics; +use function Hyperf\Coroutine\defer; + class QueueWatcher implements ListenerInterface { private Timer $timer; @@ -54,6 +56,8 @@ public function process(object $event): void } $timerId = $this->timer->tick($this->feature->getMetricsInterval(), function () { + defer(fn () => TraceMetrics::getInstance()->flush()); + $config = $this->container->get(ConfigInterface::class); $queues = array_keys($config->get('async_queue', [])); diff --git a/src/sentry/src/Metrics/Listener/RequestWatcher.php b/src/sentry/src/Metrics/Listener/RequestWatcher.php index d807d08c0..f707af2f8 100644 --- a/src/sentry/src/Metrics/Listener/RequestWatcher.php +++ b/src/sentry/src/Metrics/Listener/RequestWatcher.php @@ -59,6 +59,8 @@ public function process(object $event): void 'request_method' => $request->getMethod(), ] ); + + TraceMetrics::getInstance()->flush(); }); } }