diff --git a/src/Instrumentation/Laravel/_register.php b/src/Instrumentation/Laravel/_register.php index c265131fb..5904a0614 100644 --- a/src/Instrumentation/Laravel/_register.php +++ b/src/Instrumentation/Laravel/_register.php @@ -1,18 +1,30 @@ =7.41.3", "phan/phan": "^5.0", "php-http/mock-client": "*", "phpstan/phpstan": "^1.1", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^10.5 || ^11.3", "psalm/plugin-phpunit": "^0.19.2", - "spatie/laravel-ignition": "*", "vimeo/psalm": "6.4.0" }, "autoload": { @@ -48,11 +42,27 @@ "OpenTelemetry\\Tests\\Contrib\\Instrumentation\\Laravel\\": "tests/" } }, + "suggest": { + "ext-opentelemetry": "Required to provide auto-instrumentation hooks.", + "open-telemetry/opentelemetry-propagation-server-timing": "Automatically propagate the context to the client through server-timing headers.", + "open-telemetry/opentelemetry-propagation-traceresponse": "Automatically propagate the context to the client through trace-response headers." + }, + "extra": { + "spi-config": { + "autoload-files": true + }, + "spi": { + "OpenTelemetry\\API\\Configuration\\Config\\ComponentProvider": [ + "OpenTelemetry\\Contrib\\Instrumentation\\Laravel\\ComponentProvider\\LaravelComponentProvider" + ] + } + }, "config": { "lock": false, "sort-packages": true, "allow-plugins": { - "php-http/discovery": false + "php-http/discovery": false, + "tbachert/spi": true } } } diff --git a/src/Instrumentation/Laravel/phpunit.xml.dist b/src/Instrumentation/Laravel/phpunit.xml.dist index 0906e0e22..0af9a508c 100644 --- a/src/Instrumentation/Laravel/phpunit.xml.dist +++ b/src/Instrumentation/Laravel/phpunit.xml.dist @@ -5,6 +5,7 @@ xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd" backupGlobals="false" backupStaticAttributes="false" + bootstrap="tests/bootstrap.php" cacheResult="false" colors="false" convertErrorsToExceptions="true" @@ -41,7 +42,10 @@ - + + + + diff --git a/src/Instrumentation/Laravel/src/ComponentProvider/LaravelComponentProvider.php b/src/Instrumentation/Laravel/src/ComponentProvider/LaravelComponentProvider.php new file mode 100644 index 000000000..3d59b3368 --- /dev/null +++ b/src/Instrumentation/Laravel/src/ComponentProvider/LaravelComponentProvider.php @@ -0,0 +1,33 @@ +arrayNode('laravel') + ->canBeDisabled() + ->addDefaultsIfNotSet() + ; + } +} diff --git a/src/Instrumentation/Laravel/src/Hooks/Hook.php b/src/Instrumentation/Laravel/src/Hooks/Hook.php new file mode 100644 index 000000000..cdf41d2a4 --- /dev/null +++ b/src/Instrumentation/Laravel/src/Hooks/Hook.php @@ -0,0 +1,18 @@ +hookExecute(); + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $tracer = $context->tracerProvider->getTracer( + $instrumentation->buildProviderName('console', 'command'), + schemaUrl: Version::VERSION_1_24_0->url(), + ); + + $this->hookExecute($hookManager, $tracer); } /** @psalm-suppress PossiblyUnusedReturnValue */ - protected function hookExecute(): bool + protected function hookExecute(HookManagerInterface $hookManager, TracerInterface $tracer): void { - return hook( + $hookManager->hook( IlluminateCommand::class, 'execute', - pre: function (IlluminateCommand $command, array $params, string $class, string $function, ?string $filename, ?int $lineno) { + preHook: function (IlluminateCommand $command, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { /** @psalm-suppress ArgumentTypeCoercion */ - $builder = $this->instrumentation - ->tracer() + $builder = $tracer ->spanBuilder(sprintf('Command %s', $command->getName() ?: 'unknown')) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno); + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno); $parent = Context::getCurrent(); $span = $builder->startSpan(); @@ -45,7 +55,7 @@ protected function hookExecute(): bool return $params; }, - post: function (IlluminateCommand $command, array $params, ?int $exitCode, ?Throwable $exception) { + postHook: function (IlluminateCommand $command, array $params, ?int $exitCode, ?Throwable $exception) { $scope = Context::storage()->scope(); if (!$scope) { return; diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Console/Kernel.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Console/Kernel.php index 2a1c9e29c..18e89d784 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Console/Kernel.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Console/Kernel.php @@ -6,47 +6,56 @@ use Illuminate\Console\Command; use Illuminate\Contracts\Console\Kernel as KernelContract; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; use OpenTelemetry\API\Trace\Span; use OpenTelemetry\API\Trace\SpanKind; use OpenTelemetry\API\Trace\StatusCode; +use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\Context\Context; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Illuminate\Queue\AttributesBuilder; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait; use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\PostHookTrait; use OpenTelemetry\Contrib\Instrumentation\Laravel\LaravelInstrumentation; -use function OpenTelemetry\Instrumentation\hook; -use OpenTelemetry\SemConv\TraceAttributes; +use OpenTelemetry\SemConv\Attributes\CodeAttributes; +use OpenTelemetry\SemConv\Version; use Throwable; -class Kernel implements LaravelHook +/** @psalm-suppress UnusedClass */ +class Kernel implements Hook { use AttributesBuilder; - use LaravelHookTrait; use PostHookTrait; - public function instrument(): void - { - if (LaravelInstrumentation::shouldTraceCli()) { - $this->hookHandle(); + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $tracer = $context->tracerProvider->getTracer( + $instrumentation->buildProviderName('console', 'kernel'), + schemaUrl: Version::VERSION_1_24_0->url(), + ); + + if ($instrumentation->shouldTraceCli()) { + $this->hookHandle($hookManager, $tracer); } } /** @psalm-suppress UnusedReturnValue */ - private function hookHandle(): bool + private function hookHandle(HookManagerInterface $hookManager, TracerInterface $tracer): void { - return hook( + $hookManager->hook( KernelContract::class, 'handle', - pre: function (KernelContract $kernel, array $params, string $class, string $function, ?string $filename, ?int $lineno) { + preHook: function (KernelContract $kernel, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { /** @psalm-suppress ArgumentTypeCoercion */ - $builder = $this->instrumentation - ->tracer() + $builder = $tracer ->spanBuilder('Artisan handler') ->setSpanKind(SpanKind::KIND_PRODUCER) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno); + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno); $parent = Context::getCurrent(); $span = $builder->startSpan(); @@ -54,7 +63,7 @@ private function hookHandle(): bool return $params; }, - post: function (KernelContract $kernel, array $params, ?int $exitCode, ?Throwable $exception) { + postHook: function (KernelContract $kernel, array $params, ?int $exitCode, ?Throwable $exception) { $scope = Context::storage()->scope(); if (!$scope) { return; diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Http/Kernel.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Http/Kernel.php index b748cb715..005572c05 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Http/Kernel.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Http/Kernel.php @@ -8,65 +8,86 @@ use Illuminate\Http\Request; use Illuminate\Routing\Route; use OpenTelemetry\API\Globals; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; use OpenTelemetry\API\Trace\Span; use OpenTelemetry\API\Trace\SpanInterface; use OpenTelemetry\API\Trace\SpanKind; use OpenTelemetry\API\Trace\StatusCode; +use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\Context\Context; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait; +use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\PostHookTrait; +use OpenTelemetry\Contrib\Instrumentation\Laravel\LaravelInstrumentation; use OpenTelemetry\Contrib\Instrumentation\Laravel\Propagators\HeadersPropagator; use OpenTelemetry\Contrib\Instrumentation\Laravel\Propagators\ResponsePropagationSetter; -use function OpenTelemetry\Instrumentation\hook; -use OpenTelemetry\SemConv\TraceAttributes; +use OpenTelemetry\SemConv\Attributes\ClientAttributes; +use OpenTelemetry\SemConv\Attributes\CodeAttributes; +use OpenTelemetry\SemConv\Attributes\HttpAttributes; +use OpenTelemetry\SemConv\Attributes\NetworkAttributes; +use OpenTelemetry\SemConv\Attributes\ServerAttributes; +use OpenTelemetry\SemConv\Attributes\UrlAttributes; +use OpenTelemetry\SemConv\Attributes\UserAgentAttributes; +use OpenTelemetry\SemConv\Incubating\Attributes\HttpIncubatingAttributes; +use OpenTelemetry\SemConv\Version; use Symfony\Component\HttpFoundation\Response; use Throwable; -class Kernel implements LaravelHook +/** @psalm-suppress UnusedClass */ +class Kernel implements Hook { - use LaravelHookTrait; use PostHookTrait; - public function instrument(): void - { - $this->hookHandle(); + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $tracer = $context->tracerProvider->getTracer( + $instrumentation->buildProviderName('http', 'kernel'), + schemaUrl: Version::VERSION_1_24_0->url(), + ); + + $this->hookHandle($hookManager, $tracer, $context->propagator); } /** @psalm-suppress PossiblyUnusedReturnValue */ - protected function hookHandle(): bool - { - return hook( + protected function hookHandle( + HookManagerInterface $hookManager, + TracerInterface $tracer, + TextMapPropagatorInterface $propagator, + ): void { + $hookManager->hook( KernelContract::class, 'handle', - pre: function (KernelContract $kernel, array $params, string $class, string $function, ?string $filename, ?int $lineno) { + preHook: function (KernelContract $kernel, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer, $propagator) { $request = ($params[0] instanceof Request) ? $params[0] : null; /** @psalm-suppress ArgumentTypeCoercion */ - $builder = $this->instrumentation - ->tracer() + $builder = $tracer ->spanBuilder(sprintf('%s', $request?->method() ?? 'unknown')) ->setSpanKind(SpanKind::KIND_SERVER) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno); + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno); $parent = Context::getCurrent(); if ($request) { /** @phan-suppress-next-line PhanAccessMethodInternal */ - $parent = Globals::propagator()->extract($request, HeadersPropagator::instance()); + $parent = $propagator->extract($request, HeadersPropagator::instance()); $span = $builder ->setParent($parent) - ->setAttribute(TraceAttributes::URL_FULL, $request->fullUrl()) - ->setAttribute(TraceAttributes::HTTP_REQUEST_METHOD, $request->method()) - ->setAttribute(TraceAttributes::HTTP_REQUEST_BODY_SIZE, $request->header('Content-Length')) - ->setAttribute(TraceAttributes::URL_SCHEME, $request->getScheme()) - ->setAttribute(TraceAttributes::NETWORK_PROTOCOL_VERSION, $request->getProtocolVersion()) - ->setAttribute(TraceAttributes::NETWORK_PEER_ADDRESS, $request->server('REMOTE_ADDR')) - ->setAttribute(TraceAttributes::URL_PATH, $this->httpTarget($request)) - ->setAttribute(TraceAttributes::SERVER_ADDRESS, $this->httpHostName($request)) - ->setAttribute(TraceAttributes::SERVER_PORT, $request->getPort()) - ->setAttribute(TraceAttributes::CLIENT_PORT, $request->server('REMOTE_PORT')) - ->setAttribute(TraceAttributes::CLIENT_ADDRESS, $request->ip()) - ->setAttribute(TraceAttributes::USER_AGENT_ORIGINAL, $request->userAgent()) + ->setAttribute(UrlAttributes::URL_FULL, $request->fullUrl()) + ->setAttribute(HttpAttributes::HTTP_REQUEST_METHOD, $request->method()) + ->setAttribute(HttpIncubatingAttributes::HTTP_REQUEST_BODY_SIZE, $request->header('Content-Length')) + ->setAttribute(UrlAttributes::URL_SCHEME, $request->getScheme()) + ->setAttribute(NetworkAttributes::NETWORK_PROTOCOL_VERSION, $request->getProtocolVersion()) + ->setAttribute(NetworkAttributes::NETWORK_PEER_ADDRESS, $request->server('REMOTE_ADDR')) + ->setAttribute(UrlAttributes::URL_PATH, $this->httpTarget($request)) + ->setAttribute(ServerAttributes::SERVER_ADDRESS, $this->httpHostName($request)) + ->setAttribute(ServerAttributes::SERVER_PORT, $request->getPort()) + ->setAttribute(ClientAttributes::CLIENT_PORT, $request->server('REMOTE_PORT')) + ->setAttribute(ClientAttributes::CLIENT_ADDRESS, $request->ip()) + ->setAttribute(UserAgentAttributes::USER_AGENT_ORIGINAL, $request->userAgent()) ->startSpan(); $request->attributes->set(SpanInterface::class, $span); } else { @@ -76,7 +97,7 @@ protected function hookHandle(): bool return [$request]; }, - post: function (KernelContract $kernel, array $params, ?Response $response, ?Throwable $exception) { + postHook: function (KernelContract $kernel, array $params, ?Response $response, ?Throwable $exception) { $scope = Context::storage()->scope(); if (!$scope) { return; @@ -88,16 +109,16 @@ protected function hookHandle(): bool if ($request && $route instanceof Route) { $span->updateName("{$request->method()} /" . ltrim($route->uri, '/')); - $span->setAttribute(TraceAttributes::HTTP_ROUTE, $route->uri); + $span->setAttribute(HttpAttributes::HTTP_ROUTE, $route->uri); } if ($response) { if ($response->getStatusCode() >= 500) { $span->setStatus(StatusCode::STATUS_ERROR); } - $span->setAttribute(TraceAttributes::HTTP_RESPONSE_STATUS_CODE, $response->getStatusCode()); - $span->setAttribute(TraceAttributes::NETWORK_PROTOCOL_VERSION, $response->getProtocolVersion()); - $span->setAttribute(TraceAttributes::HTTP_RESPONSE_BODY_SIZE, $response->headers->get('Content-Length')); + $span->setAttribute(HttpAttributes::HTTP_RESPONSE_STATUS_CODE, $response->getStatusCode()); + $span->setAttribute(NetworkAttributes::NETWORK_PROTOCOL_VERSION, $response->getProtocolVersion()); + $span->setAttribute(HttpIncubatingAttributes::HTTP_RESPONSE_BODY_SIZE, $response->headers->get('Content-Length')); $prop = Globals::responsePropagator(); /** @phan-suppress-next-line PhanAccessMethodInternal */ diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Queue/Queue.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Queue/Queue.php index 403cab68b..3c5e43cf1 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Queue/Queue.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Queue/Queue.php @@ -7,49 +7,59 @@ use DateInterval; use DateTimeInterface; use Illuminate\Contracts\Queue\Queue as QueueContract; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; use OpenTelemetry\API\Trace\SpanKind; +use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\Context\Context; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Illuminate\Queue\AttributesBuilder; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait; use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\PostHookTrait; -use function OpenTelemetry\Instrumentation\hook; -use OpenTelemetry\SemConv\TraceAttributes; -use OpenTelemetry\SemConv\TraceAttributeValues; +use OpenTelemetry\Contrib\Instrumentation\Laravel\LaravelInstrumentation; +use OpenTelemetry\SemConv\Attributes\CodeAttributes; +use OpenTelemetry\SemConv\Incubating\Attributes\MessagingIncubatingAttributes; +use OpenTelemetry\SemConv\Version; use Throwable; -class Queue implements LaravelHook +/** @psalm-suppress UnusedClass */ +class Queue implements Hook { use AttributesBuilder; - use LaravelHookTrait; use PostHookTrait; - public function instrument(): void - { - $this->hookBulk(); - $this->hookLater(); - $this->hookPushRaw(); + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $tracer = $context->tracerProvider->getTracer( + $instrumentation->buildProviderName('queue'), + schemaUrl: Version::VERSION_1_24_0->url(), + ); + + $this->hookBulk($hookManager, $tracer); + $this->hookLater($hookManager, $tracer); + $this->hookPushRaw($hookManager, $tracer); } /** @psalm-suppress PossiblyUnusedReturnValue */ - protected function hookBulk(): bool + protected function hookBulk(HookManagerInterface $hookManager, TracerInterface $tracer): void { - return hook( + $hookManager->hook( QueueContract::class, 'bulk', - pre: function (QueueContract $queue, array $params, string $class, string $function, ?string $filename, ?int $lineno) { + preHook: function (QueueContract $queue, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { $attributes = array_merge([ - TraceAttributes::CODE_FUNCTION_NAME => sprintf('%s::%s', $class, $function), - TraceAttributes::CODE_FILE_PATH => $filename, - TraceAttributes::CODE_LINE_NUMBER => $lineno, - TraceAttributes::MESSAGING_BATCH_MESSAGE_COUNT => count($params[0] ?? []), + CodeAttributes::CODE_FUNCTION_NAME => sprintf('%s::%s', $class, $function), + CodeAttributes::CODE_FILE_PATH => $filename, + CodeAttributes::CODE_LINE_NUMBER => $lineno, + MessagingIncubatingAttributes::MESSAGING_BATCH_MESSAGE_COUNT => count($params[0] ?? []), ], $this->contextualMessageSystemAttributes($queue, [])); /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation - ->tracer() + $span = $tracer ->spanBuilder(vsprintf('%s %s', [ - TraceAttributeValues::MESSAGING_OPERATION_TYPE_SEND, + MessagingIncubatingAttributes::MESSAGING_OPERATION_TYPE_VALUE_SEND, /** @phan-suppress-next-line PhanUndeclaredMethod */ method_exists($queue, 'getQueue') ? $queue->getQueue($params[2] ?? null) : $queue->getConnectionName(), ])) @@ -61,19 +71,19 @@ protected function hookBulk(): bool return $params; }, - post: function (QueueContract $queue, array $params, $returnValue, ?Throwable $exception) { + postHook: function (QueueContract $queue, array $params, $returnValue, ?Throwable $exception) { $this->endSpan($exception); }, ); } /** @psalm-suppress PossiblyUnusedReturnValue */ - protected function hookLater(): bool + protected function hookLater(HookManagerInterface $hookManager, TracerInterface $tracer): void { - return hook( + $hookManager->hook( QueueContract::class, 'later', - pre: function (QueueContract $queue, array $params, string $class, string $function, ?string $filename, ?int $lineno) { + preHook: function (QueueContract $queue, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { $estimateDeliveryTimestamp = match (true) { is_int($params[0]) => (new \DateTimeImmutable())->add(new DateInterval("PT{$params[0]}S"))->getTimestamp(), $params[0] instanceof DateInterval => (new \DateTimeImmutable())->add($params[0])->getTimestamp(), @@ -82,17 +92,16 @@ protected function hookLater(): bool }; $attributes = [ - TraceAttributes::CODE_FUNCTION_NAME => sprintf('%s::%s', $class, $function), - TraceAttributes::CODE_FILE_PATH => $filename, - TraceAttributes::CODE_LINE_NUMBER => $lineno, + CodeAttributes::CODE_FUNCTION_NAME => sprintf('%s::%s', $class, $function), + CodeAttributes::CODE_FILE_PATH => $filename, + CodeAttributes::CODE_LINE_NUMBER => $lineno, 'messaging.message.delivery_timestamp' => $estimateDeliveryTimestamp, ]; /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation - ->tracer() + $span = $tracer ->spanBuilder(vsprintf('%s %s', [ - TraceAttributeValues::MESSAGING_OPERATION_TYPE_CREATE, + MessagingIncubatingAttributes::MESSAGING_OPERATION_TYPE_VALUE_CREATE, /** @phan-suppress-next-line PhanUndeclaredMethod */ method_exists($queue, 'getQueue') ? $queue->getQueue($params[2] ?? null) : $queue->getConnectionName(), ])) @@ -104,29 +113,28 @@ protected function hookLater(): bool return $params; }, - post: function (QueueContract $queue, array $params, $returnValue, ?Throwable $exception) { + postHook: function (QueueContract $queue, array $params, $returnValue, ?Throwable $exception) { $this->endSpan($exception); }, ); } /** @psalm-suppress PossiblyUnusedReturnValue */ - protected function hookPushRaw(): bool + protected function hookPushRaw(HookManagerInterface $hookManager, TracerInterface $tracer): void { - return hook( + $hookManager->hook( QueueContract::class, 'pushRaw', - pre: function (QueueContract $queue, array $params, string $_class, string $_function, ?string $_filename, ?int $_lineno) { + preHook: function (QueueContract $queue, array $params, string $_class, string $_function, ?string $_filename, ?int $_lineno) use ($tracer) { /** @phan-suppress-next-line PhanParamTooFewUnpack */ $attributes = $this->buildMessageAttributes($queue, ...$params); $parent = Context::getCurrent(); /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation - ->tracer() + $span = $tracer ->spanBuilder(vsprintf('%s %s', [ - TraceAttributeValues::MESSAGING_OPERATION_TYPE_CREATE, - $attributes[TraceAttributes::MESSAGING_DESTINATION_NAME], + MessagingIncubatingAttributes::MESSAGING_OPERATION_TYPE_VALUE_CREATE, + $attributes[MessagingIncubatingAttributes::MESSAGING_DESTINATION_NAME], ])) ->setSpanKind(SpanKind::KIND_PRODUCER) ->setAttributes($attributes) @@ -136,7 +144,7 @@ protected function hookPushRaw(): bool return $params; }, - post: function (QueueContract $queue, array $params, $returnValue, ?Throwable $exception) { + postHook: function (QueueContract $queue, array $params, $returnValue, ?Throwable $exception) { $this->endSpan($exception); }, ); diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Database/Eloquent/Model.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Database/Eloquent/Model.php index 64151b426..8375d8e28 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Database/Eloquent/Model.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Database/Eloquent/Model.php @@ -5,46 +5,56 @@ namespace OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Illuminate\Database\Eloquent; use Illuminate\Database\Eloquent\Model as EloquentModel; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; use OpenTelemetry\API\Trace\SpanKind; +use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\Context\Context; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\PostHookTrait; -use function OpenTelemetry\Instrumentation\hook; -use OpenTelemetry\SemConv\TraceAttributes; +use OpenTelemetry\Contrib\Instrumentation\Laravel\LaravelInstrumentation; +use OpenTelemetry\SemConv\Attributes\CodeAttributes; +use OpenTelemetry\SemConv\Version; use Throwable; -class Model implements LaravelHook +/** @psalm-suppress UnusedClass */ +class Model implements Hook { - use LaravelHookTrait; use PostHookTrait; - public function instrument(): void - { - $this->hookFind(); - $this->hookPerformInsert(); - $this->hookPerformUpdate(); - $this->hookDelete(); - $this->hookGetModels(); - $this->hookDestroy(); - $this->hookRefresh(); + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $tracer = $context->tracerProvider->getTracer( + $instrumentation->buildProviderName('database', 'eloquent', 'model'), + schemaUrl: Version::VERSION_1_24_0->url(), + ); + + $this->hookFind($hookManager, $tracer); + $this->hookPerformInsert($hookManager, $tracer); + $this->hookPerformUpdate($hookManager, $tracer); + $this->hookDelete($hookManager, $tracer); + $this->hookGetModels($hookManager, $tracer); + $this->hookDestroy($hookManager, $tracer); + $this->hookRefresh($hookManager, $tracer); } - private function hookFind(): void + private function hookFind(HookManagerInterface $hookManager, TracerInterface $tracer): void { /** @psalm-suppress UnusedFunctionCall */ - hook( + $hookManager->hook( \Illuminate\Database\Eloquent\Builder::class, 'find', - pre: function ($builder, array $params, string $class, string $function, ?string $filename, ?int $lineno) { + preHook: function ($builder, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { $model = $builder->getModel(); - $builder = $this->instrumentation - ->tracer() + $builder = $tracer ->spanBuilder($model::class . '::find') ->setSpanKind(SpanKind::KIND_INTERNAL) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno) + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno) ->setAttribute('laravel.eloquent.model', $model::class) ->setAttribute('laravel.eloquent.table', $model->getTable()) ->setAttribute('laravel.eloquent.operation', 'find'); @@ -55,26 +65,25 @@ private function hookFind(): void return $params; }, - post: function ($builder, array $params, $result, ?Throwable $exception) { + postHook: function ($builder, array $params, $result, ?Throwable $exception) { $this->endSpan($exception); } ); } - private function hookPerformUpdate(): void + private function hookPerformUpdate(HookManagerInterface $hookManager, TracerInterface $tracer): void { /** @psalm-suppress UnusedFunctionCall */ - hook( + $hookManager->hook( EloquentModel::class, 'performUpdate', - pre: function (EloquentModel $model, array $params, string $class, string $function, ?string $filename, ?int $lineno) { - $builder = $this->instrumentation - ->tracer() + preHook: function (EloquentModel $model, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { + $builder = $tracer ->spanBuilder($model::class . '::update') ->setSpanKind(SpanKind::KIND_INTERNAL) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno) + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno) ->setAttribute('laravel.eloquent.model', $model::class) ->setAttribute('laravel.eloquent.table', $model->getTable()) ->setAttribute('laravel.eloquent.operation', 'update'); @@ -85,26 +94,25 @@ private function hookPerformUpdate(): void return $params; }, - post: function (EloquentModel $model, array $params, $result, ?Throwable $exception) { + postHook: function (EloquentModel $model, array $params, $result, ?Throwable $exception) { $this->endSpan($exception); } ); } - private function hookPerformInsert(): void + private function hookPerformInsert(HookManagerInterface $hookManager, TracerInterface $tracer): void { /** @psalm-suppress UnusedFunctionCall */ - hook( + $hookManager->hook( EloquentModel::class, 'performInsert', - pre: function (EloquentModel $model, array $params, string $class, string $function, ?string $filename, ?int $lineno) { - $builder = $this->instrumentation - ->tracer() + preHook: function (EloquentModel $model, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { + $builder = $tracer ->spanBuilder($model::class . '::create') ->setSpanKind(SpanKind::KIND_INTERNAL) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno) + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno) ->setAttribute('laravel.eloquent.model', $model::class) ->setAttribute('laravel.eloquent.table', $model->getTable()) ->setAttribute('laravel.eloquent.operation', 'create'); @@ -115,26 +123,25 @@ private function hookPerformInsert(): void return $params; }, - post: function (EloquentModel $model, array $params, $result, ?Throwable $exception) { + postHook: function (EloquentModel $model, array $params, $result, ?Throwable $exception) { $this->endSpan($exception); } ); } - private function hookDelete(): void + private function hookDelete(HookManagerInterface $hookManager, TracerInterface $tracer): void { /** @psalm-suppress UnusedFunctionCall */ - hook( + $hookManager->hook( EloquentModel::class, 'delete', - pre: function (EloquentModel $model, array $params, string $class, string $function, ?string $filename, ?int $lineno) { - $builder = $this->instrumentation - ->tracer() + preHook: function (EloquentModel $model, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { + $builder = $tracer ->spanBuilder($model::class . '::delete') ->setSpanKind(SpanKind::KIND_INTERNAL) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno) + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno) ->setAttribute('laravel.eloquent.model', $model::class) ->setAttribute('laravel.eloquent.table', $model->getTable()) ->setAttribute('laravel.eloquent.operation', 'delete'); @@ -145,27 +152,26 @@ private function hookDelete(): void return $params; }, - post: function (EloquentModel $model, array $params, $result, ?Throwable $exception) { + postHook: function (EloquentModel $model, array $params, $result, ?Throwable $exception) { $this->endSpan($exception); } ); } - private function hookGetModels(): void + private function hookGetModels(HookManagerInterface $hookManager, TracerInterface $tracer): void { /** @psalm-suppress UnusedFunctionCall */ - hook( + $hookManager->hook( \Illuminate\Database\Eloquent\Builder::class, 'getModels', - pre: function ($builder, array $params, string $class, string $function, ?string $filename, ?int $lineno) { + preHook: function ($builder, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { $model = $builder->getModel(); - $builder = $this->instrumentation - ->tracer() + $builder = $tracer ->spanBuilder($model::class . '::get') ->setSpanKind(SpanKind::KIND_INTERNAL) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno) + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno) ->setAttribute('laravel.eloquent.model', $model::class) ->setAttribute('laravel.eloquent.table', $model->getTable()) ->setAttribute('laravel.eloquent.operation', 'get') @@ -177,30 +183,29 @@ private function hookGetModels(): void return $params; }, - post: function ($builder, array $params, $result, ?Throwable $exception) { + postHook: function ($builder, array $params, $result, ?Throwable $exception) { $this->endSpan($exception); } ); } - private function hookDestroy(): void + private function hookDestroy(HookManagerInterface $hookManager, TracerInterface $tracer): void { /** @psalm-suppress UnusedFunctionCall */ - hook( + $hookManager->hook( EloquentModel::class, 'destroy', - pre: function (string $modelClassName, array $params, string $class, string $function, ?string $filename, ?int $lineno) { + preHook: function (string $modelClassName, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { // The class-string is passed to the $model argument, because \Illuminate\Database\Eloquent\Model::destroy is static method. // Therefore, create a class instance from a class-string, and then get the table name from the getTable function. $model = new $modelClassName(); - $builder = $this->instrumentation - ->tracer() + $builder = $tracer ->spanBuilder($model::class . '::destroy') ->setSpanKind(SpanKind::KIND_INTERNAL) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno) + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno) ->setAttribute('laravel.eloquent.model', $model::class) ->setAttribute('laravel.eloquent.table', $model->getTable()) ->setAttribute('laravel.eloquent.operation', 'destroy'); @@ -211,26 +216,25 @@ private function hookDestroy(): void return $params; }, - post: function ($model, array $params, $result, ?Throwable $exception) { + postHook: function ($model, array $params, $result, ?Throwable $exception) { $this->endSpan($exception); } ); } - private function hookRefresh(): void + private function hookRefresh(HookManagerInterface $hookManager, TracerInterface $tracer): void { /** @psalm-suppress UnusedFunctionCall */ - hook( + $hookManager->hook( EloquentModel::class, 'refresh', - pre: function (EloquentModel $model, array $params, string $class, string $function, ?string $filename, ?int $lineno) { - $builder = $this->instrumentation - ->tracer() + preHook: function (EloquentModel $model, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { + $builder = $tracer ->spanBuilder($model::class . '::refresh') ->setSpanKind(SpanKind::KIND_INTERNAL) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno) + ->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, sprintf('%s::%s', $class, $function)) + ->setAttribute(CodeAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(CodeAttributes::CODE_LINE_NUMBER, $lineno) ->setAttribute('laravel.eloquent.model', $model::class) ->setAttribute('laravel.eloquent.table', $model->getTable()) ->setAttribute('laravel.eloquent.operation', 'refresh'); @@ -241,7 +245,7 @@ private function hookRefresh(): void return $params; }, - post: function (EloquentModel $model, array $params, $result, ?Throwable $exception) { + postHook: function (EloquentModel $model, array $params, $result, ?Throwable $exception) { $this->endSpan($exception); } ); diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Application.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Application.php index 794ce0f21..8e5848233 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Application.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Application.php @@ -6,8 +6,10 @@ use Illuminate\Contracts\Foundation\Application as ApplicationContract; use Illuminate\Foundation\Application as FoundationalApplication; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; +use OpenTelemetry\Contrib\Instrumentation\Laravel\LaravelInstrumentation; use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\CacheWatcher; use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\ClientRequestWatcher; use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\ExceptionWatcher; @@ -15,26 +17,37 @@ use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\QueryWatcher; use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\RedisCommand\RedisCommandWatcher; use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\Watcher; -use function OpenTelemetry\Instrumentation\hook; +use OpenTelemetry\SemConv\Version; use Throwable; -class Application implements LaravelHook +/** @psalm-suppress UnusedClass */ +class Application implements Hook { - use LaravelHookTrait; + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $logger = $context->loggerProvider->getLogger( + $instrumentation->buildProviderName('foundation', 'application'), + schemaUrl: Version::VERSION_1_24_0->url(), + ); - public function instrument(): void - { - /** @psalm-suppress UnusedFunctionCall */ - hook( + $tracer = $context->tracerProvider->getTracer( + $instrumentation->buildProviderName('foundation', 'application'), + schemaUrl: Version::VERSION_1_24_0->url(), + ); + + $hookManager->hook( FoundationalApplication::class, '__construct', - post: function (FoundationalApplication $application, array $_params, mixed $_returnValue, ?Throwable $_exception) { + postHook: function (FoundationalApplication $application, array $_params, mixed $_returnValue, ?Throwable $_exception) use ($logger, $tracer) { $this->registerWatchers($application, new CacheWatcher()); - $this->registerWatchers($application, new ClientRequestWatcher($this->instrumentation)); + $this->registerWatchers($application, new ClientRequestWatcher($tracer)); $this->registerWatchers($application, new ExceptionWatcher()); - $this->registerWatchers($application, new LogWatcher($this->instrumentation)); - $this->registerWatchers($application, new QueryWatcher($this->instrumentation)); - $this->registerWatchers($application, new RedisCommandWatcher($this->instrumentation)); + $this->registerWatchers($application, new LogWatcher($logger)); + $this->registerWatchers($application, new QueryWatcher($tracer)); + $this->registerWatchers($application, new RedisCommandWatcher($tracer)); }, ); } diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Console/ServeCommand.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Console/ServeCommand.php index 873f63b8b..af0eb97f6 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Console/ServeCommand.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Console/ServeCommand.php @@ -5,28 +5,27 @@ namespace OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Illuminate\Foundation\Console; use Illuminate\Foundation\Console\ServeCommand as FoundationServeCommand; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait; -use function OpenTelemetry\Instrumentation\hook; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; +use OpenTelemetry\Contrib\Instrumentation\Laravel\LaravelInstrumentation; /** * Instrument Laravel's local PHP development server. + * + * @psalm-suppress UnusedClass */ -class ServeCommand implements LaravelHook +class ServeCommand implements Hook { - use LaravelHookTrait; - - public function instrument(): void - { - /** @psalm-suppress UnusedFunctionCall */ - hook( + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $hookManager->hook( FoundationServeCommand::class, 'handle', - pre: static function (FoundationServeCommand $_serveCommand, array $_params, string $_class, string $_function, ?string $_filename, ?int $_lineno) { - if (!property_exists(FoundationServeCommand::class, 'passthroughVariables')) { - return; - } - + preHook: static function (FoundationServeCommand $_serveCommand, array $_params, string $_class, string $_function, ?string $_filename, ?int $_lineno) { foreach ($_ENV as $key => $_value) { if (str_starts_with($key, 'OTEL_') && !in_array($key, FoundationServeCommand::$passthroughVariables)) { FoundationServeCommand::$passthroughVariables[] = $key; diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/AttributesBuilder.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/AttributesBuilder.php index 766179f39..6314f068f 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/AttributesBuilder.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/AttributesBuilder.php @@ -8,8 +8,7 @@ use Illuminate\Queue\BeanstalkdQueue; use Illuminate\Queue\RedisQueue; use Illuminate\Queue\SqsQueue; -use OpenTelemetry\SemConv\TraceAttributes; -use OpenTelemetry\SemConv\TraceAttributeValues; +use OpenTelemetry\SemConv\Incubating\Attributes\MessagingIncubatingAttributes; trait AttributesBuilder { @@ -23,9 +22,9 @@ private function buildMessageAttributes( $payload = json_decode($rawPayload, true) ?? []; return array_merge([ - TraceAttributes::MESSAGING_DESTINATION_NAME => '(anonymous)', - TraceAttributes::MESSAGING_MESSAGE_ID => $payload['uuid'] ?? $payload['id'] ?? null, - TraceAttributes::MESSAGING_MESSAGE_ENVELOPE_SIZE => strlen($rawPayload), + MessagingIncubatingAttributes::MESSAGING_DESTINATION_NAME => '(anonymous)', + MessagingIncubatingAttributes::MESSAGING_MESSAGE_ID => $payload['uuid'] ?? $payload['id'] ?? null, + MessagingIncubatingAttributes::MESSAGING_MESSAGE_ENVELOPE_SIZE => strlen($rawPayload), 'messaging.message.job_name' => $payload['displayName'] ?? $payload['job'] ?? null, 'messaging.message.attempts' => $payload['attempts'] ?? 0, 'messaging.message.max_exceptions' => $payload['maxExceptions'] ?? null, @@ -53,24 +52,24 @@ private function contextualMessageSystemAttributes( private function beanstalkContextualAttributes(BeanstalkdQueue $queue, array $_payload, ?string $queueName = null, array $_options = [], mixed ...$_params): array { return [ - TraceAttributes::MESSAGING_SYSTEM => 'beanstalk', - TraceAttributes::MESSAGING_DESTINATION_NAME => $queue->getQueue($queueName), + MessagingIncubatingAttributes::MESSAGING_SYSTEM => 'beanstalk', + MessagingIncubatingAttributes::MESSAGING_DESTINATION_NAME => $queue->getQueue($queueName), ]; } private function redisContextualAttributes(RedisQueue $queue, array $_payload, ?string $queueName = null, array $_options = [], mixed ...$_params): array { return [ - TraceAttributes::MESSAGING_SYSTEM => 'redis', - TraceAttributes::MESSAGING_DESTINATION_NAME => $queue->getQueue($queueName), + MessagingIncubatingAttributes::MESSAGING_SYSTEM => 'redis', + MessagingIncubatingAttributes::MESSAGING_DESTINATION_NAME => $queue->getQueue($queueName), ]; } private function awsSqsContextualAttributes(SqsQueue $queue, array $_payload, ?string $queueName = null, array $_options = [], mixed ...$_params): array { return [ - TraceAttributes::MESSAGING_SYSTEM => TraceAttributeValues::MESSAGING_SYSTEM_AWS_SQS, - TraceAttributes::MESSAGING_DESTINATION_NAME => $queue->getQueue($queueName), + MessagingIncubatingAttributes::MESSAGING_SYSTEM => MessagingIncubatingAttributes::MESSAGING_SYSTEM_VALUE_AWS_SQS, + MessagingIncubatingAttributes::MESSAGING_DESTINATION_NAME => $queue->getQueue($queueName), ]; } } diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Queue.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Queue.php index c4a21f590..9ec1eba3d 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Queue.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Queue.php @@ -5,29 +5,28 @@ namespace OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Illuminate\Queue; use Illuminate\Queue\Queue as AbstractQueue; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait; -use function OpenTelemetry\Instrumentation\hook; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; +use OpenTelemetry\Contrib\Instrumentation\Laravel\LaravelInstrumentation; use Throwable; -class Queue implements LaravelHook +/** @psalm-suppress UnusedClass */ +class Queue implements Hook { use AttributesBuilder; - use LaravelHookTrait; - - public function instrument(): void - { - $this->hookAbstractQueueCreatePayloadArray(); - } /** @psalm-suppress PossiblyUnusedReturnValue */ - protected function hookAbstractQueueCreatePayloadArray(): bool - { - return hook( + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $hookManager->hook( AbstractQueue::class, 'createPayloadArray', - post: function (AbstractQueue $_queue, array $_params, array $payload, ?Throwable $_exception): array { + postHook: function (AbstractQueue $_queue, array $_params, array $payload, ?Throwable $_exception): array { TraceContextPropagator::getInstance()->inject($payload); return $payload; diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/SyncQueue.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/SyncQueue.php index 2934b2750..f4a5400fe 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/SyncQueue.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/SyncQueue.php @@ -5,51 +5,61 @@ namespace OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Illuminate\Queue; use Illuminate\Queue\SyncQueue as LaravelSyncQueue; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; use OpenTelemetry\API\Trace\SpanKind; +use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\Context\Context; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\PostHookTrait; -use function OpenTelemetry\Instrumentation\hook; -use OpenTelemetry\SemConv\TraceAttributes; +use OpenTelemetry\Contrib\Instrumentation\Laravel\LaravelInstrumentation; +use OpenTelemetry\SemConv\Attributes\CodeAttributes; +use OpenTelemetry\SemConv\Version; use Throwable; -class SyncQueue implements LaravelHook +/** @psalm-suppress UnusedClass */ +class SyncQueue implements Hook { use AttributesBuilder; - use LaravelHookTrait; use PostHookTrait; - public function instrument(): void - { - $this->hookPush(); + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $tracer = $context->tracerProvider->getTracer( + $instrumentation->buildProviderName('queue', 'sync'), + schemaUrl: Version::VERSION_1_24_0->url(), + ); + + $this->hookPush($hookManager, $tracer); } /** @psalm-suppress PossiblyUnusedReturnValue */ - protected function hookPush(): bool + protected function hookPush(HookManagerInterface $hookManager, TracerInterface $tracer): void { - return hook( + $hookManager->hook( LaravelSyncQueue::class, 'push', - pre: function (LaravelSyncQueue $queue, array $params, string $class, string $function, ?string $filename, ?int $lineno) { + preHook: function (LaravelSyncQueue $queue, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) { /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation - ->tracer() + $span = $tracer ->spanBuilder(vsprintf('%s %s', [ $queue->getConnectionName(), 'process', ])) ->setSpanKind(SpanKind::KIND_INTERNAL) ->setAttributes([ - TraceAttributes::CODE_FUNCTION_NAME => sprintf('%s::%s', $class, $function), - TraceAttributes::CODE_FILE_PATH => $filename, - TraceAttributes::CODE_LINE_NUMBER => $lineno, + CodeAttributes::CODE_FUNCTION_NAME => sprintf('%s::%s', $class, $function), + CodeAttributes::CODE_FILE_PATH => $filename, + CodeAttributes::CODE_LINE_NUMBER => $lineno, ]) ->startSpan(); Context::storage()->attach($span->storeInContext(Context::getCurrent())); }, - post: function (LaravelSyncQueue $queue, array $params, mixed $returnValue, ?Throwable $exception) { + postHook: function (LaravelSyncQueue $queue, array $params, mixed $returnValue, ?Throwable $exception) { $this->endSpan($exception); }, ); diff --git a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Worker.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Worker.php index 783b67ac0..b3c89db22 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Worker.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Worker.php @@ -6,37 +6,47 @@ use Illuminate\Contracts\Queue\Job; use Illuminate\Queue\Worker as QueueWorker; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator; use OpenTelemetry\API\Trace\Span; use OpenTelemetry\API\Trace\SpanKind; +use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\Context\Context; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook; -use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\PostHookTrait; -use function OpenTelemetry\Instrumentation\hook; -use OpenTelemetry\SemConv\TraceAttributes; -use OpenTelemetry\SemConv\TraceAttributeValues; +use OpenTelemetry\Contrib\Instrumentation\Laravel\LaravelInstrumentation; +use OpenTelemetry\SemConv\Incubating\Attributes\MessagingIncubatingAttributes; +use OpenTelemetry\SemConv\Version; use Throwable; -class Worker implements LaravelHook +/** @psalm-suppress UnusedClass */ +class Worker implements Hook { use AttributesBuilder; - use LaravelHookTrait; use PostHookTrait; - public function instrument(): void - { - $this->hookWorkerProcess(); - $this->hookWorkerGetNextJob(); + public function instrument( + LaravelInstrumentation $instrumentation, + HookManagerInterface $hookManager, + InstrumentationContext $context, + ): void { + $tracer = $context->tracerProvider->getTracer( + $instrumentation->buildProviderName('queue', 'worker'), + schemaUrl: Version::VERSION_1_24_0->url(), + ); + + $this->hookWorkerProcess($hookManager, $tracer); + $this->hookWorkerGetNextJob($hookManager, $tracer); } /** @psalm-suppress UnusedReturnValue */ - private function hookWorkerProcess(): bool + private function hookWorkerProcess(HookManagerInterface $hookManager, TracerInterface $tracer): void { - return hook( + $hookManager->hook( QueueWorker::class, 'process', - pre: function (QueueWorker $worker, array $params, string $_class, string $_function, ?string $_filename, ?int $_lineno) { + preHook: function (QueueWorker $worker, array $params, string $_class, string $_function, ?string $_filename, ?int $_lineno) use ($tracer) { $connectionName = $params[0]; /** @var Job $job */ $job = $params[1]; @@ -49,11 +59,10 @@ private function hookWorkerProcess(): bool $attributes = $this->buildMessageAttributes($queue, $job->getRawBody(), $job->getQueue()); /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation - ->tracer() + $span = $tracer ->spanBuilder(vsprintf('%s %s', [ - TraceAttributeValues::MESSAGING_OPERATION_TYPE_PROCESS, - $attributes[TraceAttributes::MESSAGING_DESTINATION_NAME], + MessagingIncubatingAttributes::MESSAGING_OPERATION_TYPE_VALUE_PROCESS, + $attributes[MessagingIncubatingAttributes::MESSAGING_DESTINATION_NAME], ])) ->setSpanKind(SpanKind::KIND_CONSUMER) ->setParent($parent) @@ -64,7 +73,7 @@ private function hookWorkerProcess(): bool return $params; }, - post: function (QueueWorker $worker, array $params, $returnValue, ?Throwable $exception) { + postHook: function (QueueWorker $worker, array $params, $returnValue, ?Throwable $exception) { $scope = Context::storage()->scope(); if (!$scope) { return; @@ -84,12 +93,12 @@ private function hookWorkerProcess(): bool } /** @psalm-suppress UnusedReturnValue */ - private function hookWorkerGetNextJob(): bool + private function hookWorkerGetNextJob(HookManagerInterface $hookManager, TracerInterface $tracer): void { - return hook( + $hookManager->hook( QueueWorker::class, 'getNextJob', - pre: function (QueueWorker $_worker, array $params, string $_class, string $_function, ?string $_filename, ?int $_lineno) { + preHook: function (QueueWorker $_worker, array $params, string $_class, string $_function, ?string $_filename, ?int $_lineno) use ($tracer) { /** @var \Illuminate\Contracts\Queue\Queue $connection */ $connection = $params[0]; $queue = $params[1]; @@ -97,11 +106,10 @@ private function hookWorkerGetNextJob(): bool $attributes = $this->buildMessageAttributes($connection, '', $queue); /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation - ->tracer() + $span = $tracer ->spanBuilder(vsprintf('%s %s', [ - TraceAttributeValues::MESSAGING_OPERATION_TYPE_RECEIVE, - $attributes[TraceAttributes::MESSAGING_DESTINATION_NAME], + MessagingIncubatingAttributes::MESSAGING_OPERATION_TYPE_VALUE_RECEIVE, + $attributes[MessagingIncubatingAttributes::MESSAGING_DESTINATION_NAME], ])) ->setSpanKind(SpanKind::KIND_CONSUMER) ->setAttributes($attributes) @@ -111,7 +119,7 @@ private function hookWorkerGetNextJob(): bool return $params; }, - post: function (QueueWorker $_worker, array $params, ?Job $job, ?Throwable $exception) { + postHook: function (QueueWorker $_worker, array $params, ?Job $job, ?Throwable $exception) { $scope = Context::storage()->scope(); if (!$scope) { return; diff --git a/src/Instrumentation/Laravel/src/Hooks/LaravelHook.php b/src/Instrumentation/Laravel/src/Hooks/LaravelHook.php deleted file mode 100644 index e36284ad8..000000000 --- a/src/Instrumentation/Laravel/src/Hooks/LaravelHook.php +++ /dev/null @@ -1,15 +0,0 @@ -instrument(); - } - - return self::$instance; - } -} diff --git a/src/Instrumentation/Laravel/src/LaravelConfiguration.php b/src/Instrumentation/Laravel/src/LaravelConfiguration.php new file mode 100644 index 000000000..48e5acf43 --- /dev/null +++ b/src/Instrumentation/Laravel/src/LaravelConfiguration.php @@ -0,0 +1,31 @@ + !class_exists(Sdk::class) || !Sdk::isInstrumentationDisabled('laravel'), + ]); + } +} diff --git a/src/Instrumentation/Laravel/src/LaravelInstrumentation.php b/src/Instrumentation/Laravel/src/LaravelInstrumentation.php index e3c210ba8..ad840b1f8 100644 --- a/src/Instrumentation/Laravel/src/LaravelInstrumentation.php +++ b/src/Instrumentation/Laravel/src/LaravelInstrumentation.php @@ -4,39 +4,43 @@ namespace OpenTelemetry\Contrib\Instrumentation\Laravel; -use OpenTelemetry\API\Instrumentation\CachedInstrumentation; -use OpenTelemetry\SDK\Common\Configuration\Configuration; +use Nevay\SPI\ServiceLoader; +use OpenTelemetry\API\Configuration\ConfigProperties; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface; +use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation; +use OpenTelemetry\API\Instrumentation\ConfigurationResolver; +use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\Hook; -class LaravelInstrumentation +class LaravelInstrumentation implements Instrumentation { - public const NAME = 'laravel'; + public const INSTRUMENTATION_NAME = 'io.opentelemetry.contrib.php.laravel'; /** @psalm-suppress PossiblyUnusedMethod */ - public static function register(): void + public function register(HookManagerInterface $hookManager, ConfigProperties $configuration, Context $context): void { - $instrumentation = new CachedInstrumentation( - 'io.opentelemetry.contrib.php.laravel', - null, - 'https://opentelemetry.io/schemas/1.32.0', - ); - - Hooks\Illuminate\Console\Command::hook($instrumentation); - Hooks\Illuminate\Contracts\Console\Kernel::hook($instrumentation); - Hooks\Illuminate\Contracts\Http\Kernel::hook($instrumentation); - Hooks\Illuminate\Contracts\Queue\Queue::hook($instrumentation); - Hooks\Illuminate\Foundation\Application::hook($instrumentation); - Hooks\Illuminate\Foundation\Console\ServeCommand::hook($instrumentation); - Hooks\Illuminate\Queue\SyncQueue::hook($instrumentation); - Hooks\Illuminate\Queue\Queue::hook($instrumentation); - Hooks\Illuminate\Queue\Worker::hook($instrumentation); - Hooks\Illuminate\Database\Eloquent\Model::hook($instrumentation); + $config = $configuration->get(LaravelConfiguration::class) ?? LaravelConfiguration::default(); + + if (! $config->enabled) { + return; + } + + foreach (ServiceLoader::load(Hook::class) as $hook) { + /** @var Hook $hook */ + $hook->instrument($this, $hookManager, $context); + } + } + + public function buildProviderName(string ...$component): string + { + return implode('.', [ + self::INSTRUMENTATION_NAME, + ...$component, + ]); } - public static function shouldTraceCli(): bool + public function shouldTraceCli(): bool { - return PHP_SAPI !== 'cli' || ( - class_exists(Configuration::class) - && Configuration::getBoolean('OTEL_PHP_TRACE_CLI_ENABLED', false) - ); + return PHP_SAPI !== 'cli' || (new ConfigurationResolver())->getBoolean('OTEL_PHP_TRACE_CLI_ENABLED'); } } diff --git a/src/Instrumentation/Laravel/src/Watchers/ClientRequestWatcher.php b/src/Instrumentation/Laravel/src/Watchers/ClientRequestWatcher.php index 3cf025dd4..7c0992076 100644 --- a/src/Instrumentation/Laravel/src/Watchers/ClientRequestWatcher.php +++ b/src/Instrumentation/Laravel/src/Watchers/ClientRequestWatcher.php @@ -10,10 +10,10 @@ use Illuminate\Http\Client\Events\ResponseReceived; use Illuminate\Http\Client\Request; use Illuminate\Http\Client\Response; -use OpenTelemetry\API\Instrumentation\CachedInstrumentation; use OpenTelemetry\API\Trace\SpanInterface; use OpenTelemetry\API\Trace\SpanKind; use OpenTelemetry\API\Trace\StatusCode; +use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\SemConv\TraceAttributes; use Symfony\Component\HttpFoundation\Response as HttpResponse; @@ -25,7 +25,7 @@ class ClientRequestWatcher extends Watcher protected array $spans = []; public function __construct( - private CachedInstrumentation $instrumentation, + private readonly TracerInterface $tracer, ) { } @@ -53,15 +53,16 @@ public function recordRequest(RequestSending $request): void if ($parsedUrl->has('query')) { $processedUrl .= '?' . $parsedUrl->get('query'); } - $span = $this->instrumentation->tracer()->spanBuilder($request->request->method()) + $span = $this->tracer + ->spanBuilder($request->request->method()) ->setSpanKind(SpanKind::KIND_CLIENT) ->setAttributes([ TraceAttributes::HTTP_REQUEST_METHOD => $request->request->method(), TraceAttributes::URL_FULL => $processedUrl, - TraceAttributes::URL_PATH => $parsedUrl['path'] ?? '', - TraceAttributes::URL_SCHEME => $parsedUrl['scheme'] ?? '', - TraceAttributes::SERVER_ADDRESS => $parsedUrl['host'] ?? '', - TraceAttributes::SERVER_PORT => $parsedUrl['port'] ?? '', + TraceAttributes::URL_PATH => $parsedUrl['path'] ?? null, + TraceAttributes::URL_SCHEME => $parsedUrl['scheme'] ?? null, + TraceAttributes::SERVER_ADDRESS => $parsedUrl['host'] ?? null, + TraceAttributes::SERVER_PORT => $parsedUrl['port'] ?? null, ]) ->startSpan(); $this->spans[$this->createRequestComparisonHash($request->request)] = $span; diff --git a/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php b/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php index 169921235..292b74281 100644 --- a/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php +++ b/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php @@ -7,7 +7,7 @@ use Illuminate\Contracts\Foundation\Application; use Illuminate\Log\Events\MessageLogged; use Illuminate\Log\LogManager; -use OpenTelemetry\API\Instrumentation\CachedInstrumentation; +use OpenTelemetry\API\Logs\LoggerInterface; use OpenTelemetry\API\Logs\LogRecord; use OpenTelemetry\API\Logs\Severity; use OpenTelemetry\SDK\Common\Exception\StackTraceFormatter; @@ -16,9 +16,10 @@ class LogWatcher extends Watcher { - private LogManager $logger; + private LogManager $logManager; + public function __construct( - private CachedInstrumentation $instrumentation, + private readonly LoggerInterface $logger, ) { } @@ -29,7 +30,7 @@ public function register(Application $app): void $app['events']->listen(MessageLogged::class, [$this, 'recordLog']); /** @phan-suppress-next-line PhanTypeArraySuspicious */ - $this->logger = $app['log']; + $this->logManager = $app['log']; } /** @@ -39,7 +40,7 @@ public function register(Application $app): void */ public function recordLog(MessageLogged $log): void { - $underlyingLogger = $this->logger->getLogger(); + $underlyingLogger = $this->logManager->getLogger(); /** * This assumes that the underlying logger (expected to be monolog) would accept `$log->level` as a string. @@ -71,14 +72,12 @@ public function recordLog(MessageLogged $log): void ] : [], ]; - $logger = $this->instrumentation->logger(); - $record = (new LogRecord($log->message)) ->setSeverityText($log->level) ->setSeverityNumber(Severity::fromPsr3($log->level)) ->setAttributes($attributes); - $logger->emit($record); + $this->logger->emit($record); } private function getExceptionFromContext(array $context): ?Throwable diff --git a/src/Instrumentation/Laravel/src/Watchers/QueryWatcher.php b/src/Instrumentation/Laravel/src/Watchers/QueryWatcher.php index 9cb5ac7b6..8d4903dcf 100644 --- a/src/Instrumentation/Laravel/src/Watchers/QueryWatcher.php +++ b/src/Instrumentation/Laravel/src/Watchers/QueryWatcher.php @@ -7,14 +7,14 @@ use Illuminate\Contracts\Foundation\Application; use Illuminate\Database\Events\QueryExecuted; use Illuminate\Support\Str; -use OpenTelemetry\API\Instrumentation\CachedInstrumentation; use OpenTelemetry\API\Trace\SpanKind; +use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\SemConv\TraceAttributes; class QueryWatcher extends Watcher { public function __construct( - private CachedInstrumentation $instrumentation, + private readonly TracerInterface $tracer, ) { } @@ -38,7 +38,8 @@ public function recordQuery(QueryExecuted $query): void $operationName = null; } /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation->tracer()->spanBuilder('sql ' . $operationName) + $span = $this->tracer + ->spanBuilder('sql ' . $operationName) ->setSpanKind(SpanKind::KIND_CLIENT) ->setStartTimestamp($this->calculateQueryStartTime($nowInNs, $query->time)) ->startSpan(); diff --git a/src/Instrumentation/Laravel/src/Watchers/RedisCommand/RedisCommandWatcher.php b/src/Instrumentation/Laravel/src/Watchers/RedisCommand/RedisCommandWatcher.php index db8e84992..52b3dc377 100644 --- a/src/Instrumentation/Laravel/src/Watchers/RedisCommand/RedisCommandWatcher.php +++ b/src/Instrumentation/Laravel/src/Watchers/RedisCommand/RedisCommandWatcher.php @@ -9,8 +9,8 @@ use Illuminate\Redis\Connections\PhpRedisConnection; use Illuminate\Redis\Connections\PredisConnection; use Illuminate\Redis\Events\CommandExecuted; -use OpenTelemetry\API\Instrumentation\CachedInstrumentation; use OpenTelemetry\API\Trace\SpanKind; +use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\Watcher; use OpenTelemetry\SemConv\TraceAttributes; use OpenTelemetry\SemConv\TraceAttributeValues; @@ -24,7 +24,7 @@ class RedisCommandWatcher extends Watcher { public function __construct( - private CachedInstrumentation $instrumentation, + private readonly TracerInterface $tracer, ) { } @@ -46,7 +46,7 @@ public function recordRedisCommand(CommandExecuted $event): void $operationName = strtoupper($event->command); /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation->tracer() + $span = $this->tracer ->spanBuilder($operationName) ->setSpanKind(SpanKind::KIND_CLIENT) ->setStartTimestamp($this->calculateQueryStartTime($nowInNs, $event->time)) @@ -82,7 +82,7 @@ private function fetchDbIndex(Connection $connection): ?int } return null; - } catch (Throwable $e) { + } catch (Throwable) { return null; } } @@ -98,7 +98,7 @@ private function fetchDbHost(Connection $connection): ?string } return null; - } catch (Throwable $e) { + } catch (Throwable) { return null; } } diff --git a/src/Instrumentation/Laravel/tests/Fixtures/ComponentProvider/LogRecordExporterInMemory.php b/src/Instrumentation/Laravel/tests/Fixtures/ComponentProvider/LogRecordExporterInMemory.php new file mode 100644 index 000000000..e9aff332b --- /dev/null +++ b/src/Instrumentation/Laravel/tests/Fixtures/ComponentProvider/LogRecordExporterInMemory.php @@ -0,0 +1,27 @@ +arrayNode('test/in_memory_exporter'); + } +} diff --git a/src/Instrumentation/Laravel/tests/Fixtures/ComponentProvider/SpanExporterInMemory.php b/src/Instrumentation/Laravel/tests/Fixtures/ComponentProvider/SpanExporterInMemory.php new file mode 100644 index 000000000..818a8173a --- /dev/null +++ b/src/Instrumentation/Laravel/tests/Fixtures/ComponentProvider/SpanExporterInMemory.php @@ -0,0 +1,27 @@ +arrayNode('test/in_memory_exporter'); + } +} diff --git a/src/Instrumentation/Laravel/tests/Fixtures/TestStorage.php b/src/Instrumentation/Laravel/tests/Fixtures/TestStorage.php new file mode 100644 index 000000000..59941c7ba --- /dev/null +++ b/src/Instrumentation/Laravel/tests/Fixtures/TestStorage.php @@ -0,0 +1,22 @@ +exchangeArray([]); + } +} diff --git a/src/Instrumentation/Laravel/tests/Fixtures/otel-instrumentation.yaml b/src/Instrumentation/Laravel/tests/Fixtures/otel-instrumentation.yaml new file mode 100644 index 000000000..6d9e1e3b3 --- /dev/null +++ b/src/Instrumentation/Laravel/tests/Fixtures/otel-instrumentation.yaml @@ -0,0 +1,18 @@ +file_format: '0.4' + +logger_provider: + processors: + - simple: + exporter: + test/in_memory_exporter: {} + +tracer_provider: + processors: + - simple: + exporter: + test/in_memory_exporter: {} + +instrumentation/development: + php: + laravel: + enabled: true diff --git a/src/Instrumentation/Laravel/tests/Integration/TestCase.php b/src/Instrumentation/Laravel/tests/Integration/TestCase.php index 2ed778d86..b2f8109a1 100644 --- a/src/Instrumentation/Laravel/tests/Integration/TestCase.php +++ b/src/Instrumentation/Laravel/tests/Integration/TestCase.php @@ -5,57 +5,25 @@ namespace OpenTelemetry\Tests\Contrib\Instrumentation\Laravel\Integration; use ArrayObject; -use OpenTelemetry\API\Instrumentation\Configurator; -use OpenTelemetry\Context\ScopeInterface; -use OpenTelemetry\SDK\Common\Attribute\Attributes; -use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory; -use OpenTelemetry\SDK\Logs\Exporter\InMemoryExporter as LogInMemoryExporter; -use OpenTelemetry\SDK\Logs\LoggerProvider; -use OpenTelemetry\SDK\Logs\Processor\SimpleLogRecordProcessor; -use OpenTelemetry\SDK\Trace\ImmutableSpan; -use OpenTelemetry\SDK\Trace\SpanExporter\InMemoryExporter as SpanInMemoryExporter; -use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; -use OpenTelemetry\SDK\Trace\TracerProvider; +use OpenTelemetry\Tests\Contrib\Instrumentation\Laravel\Fixtures\TestStorage; use Orchestra\Testbench\TestCase as BaseTestCase; +use PHPUnit\Framework\Attributes\After; +use PHPUnit\Framework\Attributes\Before; /** @psalm-suppress UnusedClass */ abstract class TestCase extends BaseTestCase { - protected ScopeInterface $scope; - /** @var ArrayObject|ImmutableSpan[] $storage */ protected ArrayObject $storage; - protected ArrayObject $loggerStorage; - protected TracerProvider $tracerProvider; - protected LoggerProvider $loggerProvider; - public function setUp(): void + #[Before] + public function setUpTestStorage(): void { - parent::setUp(); - - $this->storage = new ArrayObject(); - $this->tracerProvider = new TracerProvider( - new SimpleSpanProcessor( - new SpanInMemoryExporter($this->storage), - ), - ); - - $this->loggerProvider = new LoggerProvider( - new SimpleLogRecordProcessor( - new LogInMemoryExporter($this->storage), - ), - new InstrumentationScopeFactory(Attributes::factory()) - ); - - $this->scope = Configurator::create() - ->withTracerProvider($this->tracerProvider) - ->withLoggerProvider($this->loggerProvider) - ->activate(); + $this->storage = TestStorage::getInstance(); } - public function tearDown(): void + #[After] + public function tearDownTestStorage(): void { - parent::tearDown(); - - $this->scope->detach(); + TestStorage::reset(); } } diff --git a/src/Instrumentation/Laravel/tests/bootstrap.php b/src/Instrumentation/Laravel/tests/bootstrap.php new file mode 100644 index 000000000..581ccce71 --- /dev/null +++ b/src/Instrumentation/Laravel/tests/bootstrap.php @@ -0,0 +1,21 @@ +