From f7f7916cb83276ee02131197f3d457569b5e825d Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:19:20 +0800 Subject: [PATCH 1/8] fix(tracing): optimize HTTP response body content reading in GuzzleHttpClientAspect - Add content-type detection to only read textual responses - Limit binary/non-textual content to '[binary omitted]' to prevent memory issues - Set 8KB limit for response body content reading to avoid performance impact - Use Guzzle's Utils::copyToString for safer stream handling --- src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php index 40371ee38..90255d81d 100644 --- a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php +++ b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php @@ -124,8 +124,13 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) ]); if ($this->switcher->isTracingExtraTagEnable('http.response.body.contents')) { - $data['http.response.body.contents'] = $response->getBody()->getContents(); - $response->getBody()->isSeekable() && $response->getBody()->rewind(); + $body = $response->getBody(); + $contentType = $response->getHeaderLine('Content-Type'); + $isTextual = \preg_match('/^(text\/|application\/(json|xml|x-www-form-urlencoded))/i', $contentType) === 1; + $data['http.response.body.contents'] = $isTextual + ? \GuzzleHttp\Psr7\Utils::copyToString($body, 8192) // 8KB 上限 + : '[binary omitted]'; + $body->isSeekable() && $body->rewind(); } $span->setStatus(SpanStatus::createFromHttpStatusCode($response->getStatusCode())); From d44ddd8d3986f0f1339d58341dd2543ee54d810f Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:22:49 +0800 Subject: [PATCH 2/8] fix(tracing): improve handling of HTTP response body content in GuzzleHttpClientAspect --- .../src/Tracing/Aspect/GuzzleHttpClientAspect.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php index 90255d81d..f3cb24fa6 100644 --- a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php +++ b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php @@ -124,13 +124,16 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) ]); if ($this->switcher->isTracingExtraTagEnable('http.response.body.contents')) { - $body = $response->getBody(); $contentType = $response->getHeaderLine('Content-Type'); $isTextual = \preg_match('/^(text\/|application\/(json|xml|x-www-form-urlencoded))/i', $contentType) === 1; - $data['http.response.body.contents'] = $isTextual - ? \GuzzleHttp\Psr7\Utils::copyToString($body, 8192) // 8KB 上限 - : '[binary omitted]'; - $body->isSeekable() && $body->rewind(); + + if ($isTextual) { + $body = $response->getBody(); + $data['http.response.body.contents'] = \GuzzleHttp\Psr7\Utils::copyToString($body, 8192); // 8KB 上限 + $body->isSeekable() && $body->rewind(); + } else { + $data['http.response.body.contents'] = '[binary omitted]'; + } } $span->setStatus(SpanStatus::createFromHttpStatusCode($response->getStatusCode())); From 77c0754021b2d4e68217dfeeca49a42fc0174c8f Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:25:22 +0800 Subject: [PATCH 3/8] fix(tracing): standardize request and response data keys in GuzzleHttpClientAspect --- .../src/Aspect/GuzzleHttpClientAspect.php | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/sentry/src/Aspect/GuzzleHttpClientAspect.php b/src/sentry/src/Aspect/GuzzleHttpClientAspect.php index aa4d1f1b6..120042766 100644 --- a/src/sentry/src/Aspect/GuzzleHttpClientAspect.php +++ b/src/sentry/src/Aspect/GuzzleHttpClientAspect.php @@ -57,25 +57,25 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) $proceedingJoinPoint->arguments['keys']['options']['on_stats'] = function (TransferStats $stats) use ($onStats, $guzzleConfig, $options) { $request = $stats->getRequest(); $response = $stats->getResponse(); - $uri = $request->getUri()->__toString(); - $data = []; - $data['config'] = $guzzleConfig; - // request - $data['request.method'] = $request->getMethod(); - $data['request.body.size'] = strlen($options['body'] ?? ''); - $data['request.full_url'] = (string) $request->getUri(); - $data['request.path'] = $request->getUri()->getPath(); - $data['request.scheme'] = $request->getUri()->getScheme(); - $data['request.host'] = $request->getUri()->getHost(); - $data['request.port'] = $request->getUri()->getPort(); - $data['request.user_agent'] = $request->getHeaderLine('User-Agent'); // updated key for consistency - $data['request.headers'] = $request->getHeaders(); - // response - $data['response.status'] = $response?->getStatusCode(); - $data['response.reason'] = $response?->getReasonPhrase(); - $data['response.headers'] = $response?->getHeaders(); - $data['duration'] = $stats->getTransferTime() * 1000; + $data = [ + 'config' => $guzzleConfig, + // request + 'http.request.method' => $request->getMethod(), + 'http.request.body.size' => strlen($options['body'] ?? ''), + 'http.request.full_url' => (string) $request->getUri(), + 'http.request.path' => $request->getUri()->getPath(), + 'http.request.scheme' => $request->getUri()->getScheme(), + 'http.request.host' => $request->getUri()->getHost(), + 'http.request.port' => $request->getUri()->getPort(), + 'http.request.user_agent' => $request->getHeaderLine('User-Agent'), // updated key for consistency + 'http.request.headers' => $request->getHeaders(), + // response + 'http.response.status_code' => $response?->getStatusCode(), + 'http.response.reason' => $response?->getReasonPhrase(), + 'http.response.headers' => $response?->getHeaders(), + 'duration' => $stats->getTransferTime() * 1000, + ]; Integration::addBreadcrumb(new Breadcrumb( Breadcrumb::LEVEL_INFO, From 8eef3828a2abae2c1de73841c54e2f2dd5a36b9c Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:26:16 +0800 Subject: [PATCH 4/8] fix(tracing): add HTTP response body size to breadcrumb data in GuzzleHttpClientAspect --- src/sentry/src/Aspect/GuzzleHttpClientAspect.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/src/Aspect/GuzzleHttpClientAspect.php b/src/sentry/src/Aspect/GuzzleHttpClientAspect.php index 120042766..833a5c399 100644 --- a/src/sentry/src/Aspect/GuzzleHttpClientAspect.php +++ b/src/sentry/src/Aspect/GuzzleHttpClientAspect.php @@ -72,6 +72,7 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) 'http.request.headers' => $request->getHeaders(), // response 'http.response.status_code' => $response?->getStatusCode(), + 'http.response.body.size' => $response?->getBody()->getSize() ?? 0, 'http.response.reason' => $response?->getReasonPhrase(), 'http.response.headers' => $response?->getHeaders(), 'duration' => $stats->getTransferTime() * 1000, From 2d4291e17e7386823a4b546b5e1fa34b76c12d2b Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:27:04 +0800 Subject: [PATCH 5/8] fix(tracing): add comment to clarify on_stats option override in GuzzleHttpClientAspect --- src/sentry/src/Aspect/GuzzleHttpClientAspect.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/src/Aspect/GuzzleHttpClientAspect.php b/src/sentry/src/Aspect/GuzzleHttpClientAspect.php index 833a5c399..edf94cbec 100644 --- a/src/sentry/src/Aspect/GuzzleHttpClientAspect.php +++ b/src/sentry/src/Aspect/GuzzleHttpClientAspect.php @@ -54,6 +54,7 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) $onStats = $options['on_stats'] ?? null; + // Override the on_stats option to record the request. $proceedingJoinPoint->arguments['keys']['options']['on_stats'] = function (TransferStats $stats) use ($onStats, $guzzleConfig, $options) { $request = $stats->getRequest(); $response = $stats->getResponse(); From ec1260d32a84ab543621312a9b69fd8a5952cf5b Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:29:20 +0800 Subject: [PATCH 6/8] fix(tracing): add additional response header fields for content length in GuzzleHttpClientAspect --- src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php index f3cb24fa6..a6f61cc61 100644 --- a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php +++ b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php @@ -121,6 +121,9 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) 'http.response.reason' => $response->getReasonPhrase(), 'http.response.headers' => $response->getHeaders(), 'http.response.body.size' => $response->getBody()->getSize() ?? 0, + 'http.response_content_length' => $response->getHeaderLine('Content-Length'), + 'http.decoded_response_content_length' => $response->getHeaderLine('X-Decoded-Content-Length'), + 'http.response_transfer_size' => $response->getHeaderLine('Content-Length'), ]); if ($this->switcher->isTracingExtraTagEnable('http.response.body.contents')) { From 47e8176cddce4a177f1c5eff2be1c1a5d5de262a Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:34:47 +0800 Subject: [PATCH 7/8] fix(tracing): improve handling of HTTP response body content in GuzzleHttpClientAspect --- .../src/Tracing/Aspect/GuzzleHttpClientAspect.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php index a6f61cc61..ee092608e 100644 --- a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php +++ b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php @@ -127,13 +127,15 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) ]); if ($this->switcher->isTracingExtraTagEnable('http.response.body.contents')) { - $contentType = $response->getHeaderLine('Content-Type'); - $isTextual = \preg_match('/^(text\/|application\/(json|xml|x-www-form-urlencoded))/i', $contentType) === 1; + $isTextual = \preg_match( + '/^(text\/|application\/(json|xml|x-www-form-urlencoded))/i', + $response->getHeaderLine('Content-Type') + ) === 1; + $body = $response->getBody(); - if ($isTextual) { - $body = $response->getBody(); + if ($isTextual && $body->isSeekable()) { $data['http.response.body.contents'] = \GuzzleHttp\Psr7\Utils::copyToString($body, 8192); // 8KB 上限 - $body->isSeekable() && $body->rewind(); + $body->rewind(); } else { $data['http.response.body.contents'] = '[binary omitted]'; } From be31ce8e9a6ceda48af03b0e64862b1e3e095ebe Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:36:12 +0800 Subject: [PATCH 8/8] fix(tracing): preserve position of response body stream in GuzzleHttpClientAspect --- src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php index ee092608e..67637e3c4 100644 --- a/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php +++ b/src/sentry/src/Tracing/Aspect/GuzzleHttpClientAspect.php @@ -134,8 +134,9 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) $body = $response->getBody(); if ($isTextual && $body->isSeekable()) { + $pos = $body->tell(); $data['http.response.body.contents'] = \GuzzleHttp\Psr7\Utils::copyToString($body, 8192); // 8KB 上限 - $body->rewind(); + $body->seek($pos); } else { $data['http.response.body.contents'] = '[binary omitted]'; }