From 3475a13a64fd6d437897ba5cde7b80bc012cc0a5 Mon Sep 17 00:00:00 2001 From: guandeng Date: Mon, 20 Nov 2023 14:14:06 +0800 Subject: [PATCH] Add record rpc server (#442) * Add record rpc server * Improve telescope middleware * optimize code * Fix TelescopeMiddleware to record service or request based on server type * Update telescope README.md to include rpc server/client * Update README.md * Add record rpc server * Improve telescope middleware * optimize code * Fix TelescopeMiddleware to record service or request based on server type * Update telescope README.md to include rpc server/client * Update README.md * Fix RequestHandledListener to record service or --------- Co-authored-by: Deeka Wong <8337659+huangdijia@users.noreply.github.com> --- composer.json | 1 + src/telescope/README.md | 6 +- src/telescope/publish/telescope.php | 1 + src/telescope/src/Aspect/HttpClientAspect.php | 71 ++++++++++++------- src/telescope/src/Aspect/RpcAspect.php | 45 ++++++++++++ src/telescope/src/ConfigProvider.php | 1 + .../src/Listener/RequestHandledListener.php | 45 ++++++++++-- .../src/Middleware/TelescopeMiddleware.php | 53 ++++++++++---- 8 files changed, 178 insertions(+), 45 deletions(-) create mode 100644 src/telescope/src/Aspect/RpcAspect.php diff --git a/composer.json b/composer.json index ddec8c06f..ed27c9588 100644 --- a/composer.json +++ b/composer.json @@ -67,6 +67,7 @@ "hyperf/framework": "~3.0.0", "hyperf/grpc-client": "~3.0.0", "hyperf/grpc-server": "~3.0.0", + "hyperf/json-rpc": "~3.0.0", "hyperf/guzzle": "~3.0.0", "hyperf/http-server": "~3.0.20", "hyperf/http2-client": "~3.0.0", diff --git a/src/telescope/README.md b/src/telescope/README.md index 1cb7914da..b7fbf42a5 100644 --- a/src/telescope/README.md +++ b/src/telescope/README.md @@ -7,13 +7,14 @@ An elegant debug assistant for the hyperf framework. - [x] request - [x] exception - [x] sql -- [x] grpc client +- [x] grpc server/client - [x] redis - [x] log - [x] command - [x] event -- [x] http client +- [x] guzzle - [x] cache +- [x] rpc server/client ## Installation @@ -80,6 +81,7 @@ TELESCOPE_ENABLE_JOB=true TELESCOPE_ENABLE_DB=true TELESCOPE_ENABLE_GUZZLE=true TELESCOPE_ENABLE_CACHE=true +TELESCOPE_ENABLE_RPC=true TELESCOPE_SERVER_ENABLE=true ``` diff --git a/src/telescope/publish/telescope.php b/src/telescope/publish/telescope.php index 5349e1c2a..5916795f2 100644 --- a/src/telescope/publish/telescope.php +++ b/src/telescope/publish/telescope.php @@ -28,6 +28,7 @@ 'db' => env('TELESCOPE_ENABLE_DB', false), 'guzzle' => env('TELESCOPE_ENABLE_GUZZLE', false), 'cache' => env('TELESCOPE_ENABLE_CACHE', false), + 'rpc' => env('TELESCOPE_ENABLE_RPC', false), ], 'timezone' => env('TELESCOPE_TIMEZONE', 'Asia/Shanghai'), 'database' => [ diff --git a/src/telescope/src/Aspect/HttpClientAspect.php b/src/telescope/src/Aspect/HttpClientAspect.php index 11778f757..f29f60aee 100644 --- a/src/telescope/src/Aspect/HttpClientAspect.php +++ b/src/telescope/src/Aspect/HttpClientAspect.php @@ -17,9 +17,11 @@ use GuzzleHttp\Client; use Hyperf\Di\Aop\AbstractAspect; use Hyperf\Di\Aop\ProceedingJoinPoint; +use Psr\Http\Message\ResponseInterface; -use function Hyperf\Tappable\tap; - +/** + * @property array $config + */ class HttpClientAspect extends AbstractAspect { public array $classes = [ @@ -33,29 +35,46 @@ public function __construct(protected SwitchManager $switcherManager) public function process(ProceedingJoinPoint $proceedingJoinPoint) { - return tap($proceedingJoinPoint->process(), function ($result) use ($proceedingJoinPoint) { - if (! $this->switcherManager->isEnable('guzzle')) { - return; - } - $options = $proceedingJoinPoint->arguments['keys']['options']; - if (isset($options['no_aspect']) && $options['no_aspect'] === true) { - return; - } - $arguments = $proceedingJoinPoint->arguments; - $method = $arguments['keys']['method'] ?? 'Null'; - $uri = $arguments['keys']['uri'] ?? 'Null'; - $headers = $options['headers'] ?? []; - - Telescope::recordClientRequest(IncomingEntry::make([ - 'method' => $method, - 'uri' => $uri, - 'headers' => $headers, - 'payload' => '', - 'response_status' => 0, - 'response_headers' => '', - 'response' => '', - 'duration' => 0, - ])); - }); + if (! $this->switcherManager->isEnable('guzzle')) { + return $proceedingJoinPoint->process(); + } + $startTime = microtime(true); + $instance = $proceedingJoinPoint->getInstance(); + $arguments = $proceedingJoinPoint->arguments; + $options = $arguments['keys']['options'] ?? []; + $guzzleConfig = (fn () => $this->config ?? [])->call($instance); + + if (($options['no_telescope_aspect'] ?? null) === true || ($guzzleConfig['no_telescope_aspect'] ?? null) === true) { + return $proceedingJoinPoint->process(); + } + + // Disable the aspect for the requestAsync method. + if ($proceedingJoinPoint->methodName == 'request') { + $proceedingJoinPoint->arguments['keys']['options']['no_telescope_aspect'] = true; + } + + $arguments = $proceedingJoinPoint->arguments; + $method = $arguments['keys']['method'] ?? 'GET'; + $uri = $arguments['keys']['uri'] ?? ''; + $headers = $options['headers'] ?? []; + + $result = $proceedingJoinPoint->process(); + + $response = []; + if ($result instanceof ResponseInterface) { + $response['status'] = $result->getStatusCode(); + $response['reason'] = $result->getReasonPhrase(); + $response['headers'] = $result->getHeaders(); + } + Telescope::recordClientRequest(IncomingEntry::make([ + 'method' => $method, + 'uri' => $uri, + 'headers' => $headers, + 'response_status' => $response['status'] ?? 0, + 'response_headers' => $response['headers'] ?? '', + 'response' => $response, + 'duration' => floor((microtime(true) - $startTime) * 1000), + ])); + return $result; } } diff --git a/src/telescope/src/Aspect/RpcAspect.php b/src/telescope/src/Aspect/RpcAspect.php new file mode 100644 index 000000000..381229dc8 --- /dev/null +++ b/src/telescope/src/Aspect/RpcAspect.php @@ -0,0 +1,45 @@ +process(), function () { + if (static::class == self::class && $this->switcherManager->isEnable('rpc') === false) { + return; + } + + $carrier = []; + $carrier['batch-id'] = TelescopeContext::getBatchId(); + $this->context->set('telescope.carrier', $carrier); + }); + } +} diff --git a/src/telescope/src/ConfigProvider.php b/src/telescope/src/ConfigProvider.php index cb1584c1d..247d48cf8 100644 --- a/src/telescope/src/ConfigProvider.php +++ b/src/telescope/src/ConfigProvider.php @@ -38,6 +38,7 @@ public function __invoke(): array Aspect\HttpClientAspect::class, Aspect\LogAspect::class, Aspect\RedisAspect::class, + Aspect\RpcAspect::class, ], 'annotations' => [ 'scan' => [ diff --git a/src/telescope/src/Listener/RequestHandledListener.php b/src/telescope/src/Listener/RequestHandledListener.php index f4049396b..4d959d4ac 100644 --- a/src/telescope/src/Listener/RequestHandledListener.php +++ b/src/telescope/src/Listener/RequestHandledListener.php @@ -16,20 +16,30 @@ use FriendsOfHyperf\Telescope\Telescope; use FriendsOfHyperf\Telescope\TelescopeContext; use Hyperf\Collection\Arr; +use Hyperf\Context\ApplicationContext; use Hyperf\Contract\ConfigInterface; use Hyperf\Event\Contract\ListenerInterface; use Hyperf\HttpServer\Event\RequestReceived; use Hyperf\HttpServer\Event\RequestTerminated; use Hyperf\HttpServer\Router\Dispatched; +use Hyperf\Rpc; +use Hyperf\Server\Event; use Hyperf\Stringable\Str; +use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Swow\Psr7\Message\ResponsePlusInterface; +use function Hyperf\Collection\collect; +use function Hyperf\Config\config; + class RequestHandledListener implements ListenerInterface { - public function __construct(protected SwitchManager $switchManager, protected ConfigInterface $config) - { + public function __construct( + protected ContainerInterface $container, + protected ConfigInterface $config, + protected SwitchManager $switchManager + ) { } public function listen(): array @@ -57,10 +67,14 @@ public function requestReceived(RequestReceived $event) $request = $event->request; if (! $batchId = $request->getHeaderLine('batch-id')) { - $batchId = Str::orderedUuid()->toString(); - } else { + $batchId = $this->getRpcBatchId(); + } + + if ($batchId) { $subBatchId = Str::orderedUuid()->toString(); TelescopeContext::setSubBatchId($subBatchId); + } else { + $batchId = Str::orderedUuid()->toString(); } TelescopeContext::setBatchId($batchId); @@ -83,6 +97,7 @@ public function requestHandled(RequestTerminated $event) if ($this->incomingRequest($psr7Request)) { /** @var Dispatched $dispatched */ $dispatched = $psr7Request->getAttribute(Dispatched::class); + $serverName = $dispatched->serverName ?? 'http'; $entry = IncomingEntry::make([ 'ip_address' => $psr7Request->getServerParams()['remote_addr'] ?? 'unknown', @@ -99,7 +114,11 @@ public function requestHandled(RequestTerminated $event) 'memory' => round(memory_get_peak_usage(true) / 1024 / 1025, 1), ]); - if (Str::contains($psr7Request->getHeaderLine('content-type'), 'application/grpc')) { + $serverConfig = collect(config('server.servers'))->firstWhere('name', $serverName); + $handlerClass = $serverConfig['callbacks'][Event::ON_RECEIVE][0] ?? $serverConfig['callbacks'][Event::ON_REQUEST][0] ?? null; + $handler = is_string($handlerClass) && $this->container->has($handlerClass) ? $this->container->get($handlerClass) : null; + + if ($handler && ($handler instanceof \Hyperf\RpcServer\Server || $handler instanceof \Hyperf\GrpcServer\Server || $handler instanceof \Hyperf\JsonRpc\HttpServer)) { Telescope::recordService($entry); } else { Telescope::recordRequest($entry); @@ -179,4 +198,20 @@ protected function hideParameters(array $data, array $hidden): array return $data; } + + protected function getRpcBatchId(): string + { + $carrier = $this->getRpcContext(); + return $carrier['batch-id'] ?? ''; + } + + protected function getRpcContext(): array + { + $container = ApplicationContext::getContainer(); + if (! $container->has(Rpc\Context::class)) { + return []; + } + $rpcContext = $container->get(Rpc\Context::class); + return (array) $rpcContext->get('telescope.carrier'); + } } diff --git a/src/telescope/src/Middleware/TelescopeMiddleware.php b/src/telescope/src/Middleware/TelescopeMiddleware.php index 607d5f3a6..0248b0281 100644 --- a/src/telescope/src/Middleware/TelescopeMiddleware.php +++ b/src/telescope/src/Middleware/TelescopeMiddleware.php @@ -16,19 +16,26 @@ use FriendsOfHyperf\Telescope\Telescope; use FriendsOfHyperf\Telescope\TelescopeContext; use Hyperf\Collection\Arr; +use Hyperf\Context\ApplicationContext; use Hyperf\Contract\ConfigInterface; use Hyperf\HttpServer\Router\Dispatched; +use Hyperf\Rpc; +use Hyperf\Server\Event; use Hyperf\Stringable\Str; +use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use function Hyperf\Collection\collect; +use function Hyperf\Config\config; use function Hyperf\Coroutine\defer; class TelescopeMiddleware implements MiddlewareInterface { public function __construct( + protected ContainerInterface $container, protected ConfigInterface $config, protected SwitchManager $switchManager ) { @@ -40,20 +47,21 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface return $handler->handle($request); } - $batchId = $request->getHeaderLine('batch-id'); - if (! $batchId) { - $batchId = Str::orderedUuid()->toString(); - } else { + if (! $batchId = $request->getHeaderLine('batch-id')) { + $batchId = $this->getRpcBatchId(); + } + + if ($batchId) { $subBatchId = Str::orderedUuid()->toString(); TelescopeContext::setSubBatchId($subBatchId); + } else { + $batchId = Str::orderedUuid()->toString(); } + TelescopeContext::setBatchId($batchId); $response = $handler->handle($request); - - if ($batchId) { - $response = $response->withHeader('batch-id', $batchId); - } + $response = $response->withHeader('batch-id', $batchId); defer(fn () => $this->requestHandled($request, $response)); @@ -73,7 +81,8 @@ public function requestHandled($request, $response) if ($this->incomingRequest($psr7Request)) { /** @var Dispatched $dispatched */ $dispatched = $psr7Request->getAttribute(Dispatched::class); - $middlewares = $this->config->get('middlewares.' . ($dispatched->serverName ?? 'http'), []); + $serverName = $dispatched->serverName ?? 'http'; + $middlewares = $this->config->get('middlewares.' . $serverName, []); $entry = IncomingEntry::make([ 'ip_address' => $psr7Request->getServerParams()['remote_addr'], @@ -90,7 +99,11 @@ public function requestHandled($request, $response) 'memory' => round(memory_get_peak_usage(true) / 1024 / 1025, 1), ]); - if (Str::contains($psr7Request->getHeaderLine('content-type'), 'application/grpc')) { + $serverConfig = collect(config('server.servers'))->firstWhere('name', $serverName); + $handlerClass = $serverConfig['callbacks'][Event::ON_RECEIVE][0] ?? $serverConfig['callbacks'][Event::ON_REQUEST][0] ?? null; + $handler = is_string($handlerClass) && $this->container->has($handlerClass) ? $this->container->get($handlerClass) : null; + + if ($handler && ($handler instanceof \Hyperf\RpcServer\Server || $handler instanceof \Hyperf\GrpcServer\Server || $handler instanceof \Hyperf\JsonRpc\HttpServer)) { Telescope::recordService($entry); } else { Telescope::recordRequest($entry); @@ -112,7 +125,7 @@ protected function incomingRequest(ServerRequestInterface $psr7Request): bool return true; } - protected function response(ResponseInterface $response): string + protected function response(ResponseInterface $response): string|array { $stream = $response->getBody(); if ($stream->isSeekable()) { @@ -129,7 +142,7 @@ protected function response(ResponseInterface $response): string ) { return $this->contentWithinLimits($content) ? $this->hideParameters(json_decode($content, true), Telescope::$hiddenResponseParameters) - : 'Purged By Telescope'; + : 'Purged By Hyperf Telescope'; } if (Str::startsWith(strtolower($response->getHeaderLine('content-type') ?? ''), 'text/plain')) { return $this->contentWithinLimits($content) ? $content : 'Purged By Hyperf Telescope'; @@ -166,4 +179,20 @@ protected function hideParameters(array $data, array $hidden): array return $data; } + + protected function getRpcBatchId(): string + { + $carrier = $this->getRpcContext(); + return $carrier['batch-id'] ?? ''; + } + + protected function getRpcContext(): array + { + $container = ApplicationContext::getContainer(); + if (! $container->has(Rpc\Context::class)) { + return []; + } + $rpcContext = $container->get(Rpc\Context::class); + return (array) $rpcContext->get('telescope.carrier'); + } }