diff --git a/composer.json b/composer.json index b56e54cbb99..0bc7e85cbda 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,7 @@ "opentracing/opentracing": "1.0.0-beta5" }, "require-dev": { + "guzzlehttp/guzzle": "^5.0", "phpcompatibility/php-compatibility": "^9.0", "phpcompatibility/phpcompatibility-passwordcompat": "^1.0", "phpcompatibility/phpcompatibility-symfony": "*", diff --git a/src/DDTrace/Integrations/Guzzle/v5/GuzzleIntegration.php b/src/DDTrace/Integrations/Guzzle/v5/GuzzleIntegration.php new file mode 100644 index 00000000000..baaaa10bc5b --- /dev/null +++ b/src/DDTrace/Integrations/Guzzle/v5/GuzzleIntegration.php @@ -0,0 +1,28 @@ +setTag('http.method', $args[0]->getMethod()); + }); + } + + public static function setDefaultTags(Span $span, $method) + { + parent::setDefaultTags($span, $method); + $span->setTag(Tags\SPAN_TYPE, Types\GUZZLE); + $span->setTag(Tags\SERVICE_NAME, 'guzzle'); + } +} diff --git a/src/DDTrace/Integrations/Integration.php b/src/DDTrace/Integrations/Integration.php new file mode 100644 index 00000000000..8d5825b33ef --- /dev/null +++ b/src/DDTrace/Integrations/Integration.php @@ -0,0 +1,62 @@ +startActiveSpan($className . '.' . $method); + $span = $scope->getSpan(); + $integrationClass::setDefaultTags($span, $method); + if (null !== $spanMutator) { + $spanMutator($span, $args); + } + + $returnVal = null; + $thrownException = null; + try { + $returnVal = call_user_func_array([$this, $method], $args); + } catch (\Exception $e) { + $span->setError($e); + $thrownException = $e; + } + $scope->close(); + if (null !== $thrownException) { + throw $thrownException; + } + return $returnVal; + }); + } + + public static function setDefaultTags(Span $span, $method) + { + $span->setTag(Tags\RESOURCE_NAME, $method); + } +} diff --git a/src/DDTrace/Types.php b/src/DDTrace/Types.php index e17a96082cf..dfd9ac4502f 100644 --- a/src/DDTrace/Types.php +++ b/src/DDTrace/Types.php @@ -8,6 +8,7 @@ const SQL = 'sql'; const CASSANDRA = 'cassandra'; +const GUZZLE = 'guzzle'; const MEMCACHED = 'memcached'; const MONGO = 'mongodb'; const REDIS = 'redis'; diff --git a/tests/Integration/Integrations/Guzzle/v5/GuzzleIntegrationTest.php b/tests/Integration/Integrations/Guzzle/v5/GuzzleIntegrationTest.php new file mode 100644 index 00000000000..a4b80d0f2f8 --- /dev/null +++ b/tests/Integration/Integrations/Guzzle/v5/GuzzleIntegrationTest.php @@ -0,0 +1,71 @@ + 200]); + $this->client = new Client(['handler' => $handler]); + } + + /** + * @dataProvider providerHttpMethods + */ + public function testAliasMethods($method) + { + $traces = $this->isolateTracer(function () use ($method) { + $this->client->$method('http://example.com'); + }); + $this->assertSpans($traces, [ + SpanAssertion::build('GuzzleHttp\Client.send', 'guzzle', 'guzzle', 'send') + ->withExactTags([ + 'http.method' => strtoupper($method), + ]), + ]); + } + + public function providerHttpMethods() + { + return [ + ['get'], + ['delete'], + ['head'], + ['options'], + ['patch'], + ['post'], + ['put'], + ]; + } + + public function testSend() + { + $traces = $this->isolateTracer(function () { + $request = new Request('put', 'http://example.com'); + $this->client->send($request); + }); + $this->assertSpans($traces, [ + SpanAssertion::build('GuzzleHttp\Client.send', 'guzzle', 'guzzle', 'send') + ->withExactTags([ + 'http.method' => 'PUT', + ]), + ]); + } +}