Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 60 additions & 30 deletions src/DDTrace/Integrations/Guzzle/GuzzleIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace DDTrace\Integrations\Guzzle;

use DDTrace\HookData;
use DDTrace\Http\Urls;
use DDTrace\Integrations\HttpClientIntegrationHelper;
use DDTrace\Integrations\Integration;
Expand Down Expand Up @@ -33,10 +34,65 @@ public static function handlePromiseResponse($response, SpanData $span)

public static function init(): int
{
/* Until we support both pre- and post- hooks on the same function, do
* not send distributed tracing headers; curl will almost guaranteed do
* it for us anyway. Just do a post-hook to get the response.
*/
\DDTrace\install_hook(
'GuzzleHttp\Client::transfer',
static function (HookData $hook) {
// Note: We must ALWAYS call overrideArguments() to prevent JIT compilation issues.
// See ext/hook/uhook.c: "hooks wishing to override args must do so unconditionally"

$modified = false;

if (isset($hook->args[0])) {
$request = $hook->args[0];

if ($request instanceof \Psr\Http\Message\RequestInterface) {
$dtHeaders = \DDTrace\generate_distributed_tracing_headers();

if (!empty($dtHeaders)) {
foreach ($dtHeaders as $name => $value) {
if (!$request->hasHeader($name)) {
$request = $request->withHeader($name, $value);
$modified = true;
}
}

if ($modified) {
$hook->args[0] = $request;
}
}
}
}

// CRITICAL: Always call overrideArguments to prevent JIT from breaking header injection
$hook->overrideArguments($hook->args);
},
static function (HookData $hook) {
$span = $hook->span();
if (!$span) {
return;
}

$span->resource = 'transfer';
$span->name = 'GuzzleHttp\Client.transfer';
Integration::handleInternalSpanServiceName($span, self::NAME);
$span->type = Type::HTTP_CLIENT;
$span->meta[Tag::SPAN_KIND] = Tag::SPAN_KIND_VALUE_CLIENT;
$span->meta[Tag::COMPONENT] = self::NAME;
$span->peerServiceSources = HttpClientIntegrationHelper::PEER_SERVICE_SOURCES;

if (isset($hook->args[0])) {
self::addRequestInfo($span, $hook->args[0]);
}

if (isset($hook->returned)) {
$response = $hook->returned;
if (\is_a($response, 'GuzzleHttp\Promise\PromiseInterface')) {
self::handlePromiseResponse($response, $span);
}
}
}
);

\DDTrace\trace_method(
'GuzzleHttp\Client',
'send',
Expand All @@ -52,8 +108,6 @@ static function (SpanData $span, $args, $retval) {
\defined('GuzzleHttp\ClientInterface::VERSION')
&& substr(\GuzzleHttp\ClientInterface::VERSION, 0, 2) === '5.'
) {
// On Guzzle 6+, we do not need to generate peer.service for the send span,
// as the terminal span is 'transfer'
$span->peerServiceSources = HttpClientIntegrationHelper::PEER_SERVICE_SOURCES;
}

Expand All @@ -80,30 +134,6 @@ static function (SpanData $span, $args, $retval) {
}
);

\DDTrace\trace_method(
'GuzzleHttp\Client',
'transfer',
static function (SpanData $span, $args, $retval) {
$span->resource = 'transfer';
$span->name = 'GuzzleHttp\Client.transfer';
Integration::handleInternalSpanServiceName($span, self::NAME);
$span->type = Type::HTTP_CLIENT;
$span->meta[Tag::SPAN_KIND] = Tag::SPAN_KIND_VALUE_CLIENT;
$span->meta[Tag::COMPONENT] = self::NAME;
$span->peerServiceSources = HttpClientIntegrationHelper::PEER_SERVICE_SOURCES;

if (isset($args[0])) {
self::addRequestInfo($span, $args[0]);
}
if (isset($retval)) {
$response = $retval;
if (\is_a($response, 'GuzzleHttp\Promise\PromiseInterface')) {
self::handlePromiseResponse($response, $span);
}
}
}
);

return Integration::LOADED;
}

Expand Down
Loading