diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index ee1d2ee4..41c7ab67 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -77,6 +77,8 @@ jobs: php-version: 7.4 - project: 'Instrumentation/Laravel' php-version: 7.4 + - project: 'Instrumentation/Laravel' + php-version: 8.0 - project: 'Instrumentation/CodeIgniter' php-version: 7.4 - project: 'Instrumentation/Yii' diff --git a/src/Instrumentation/Laravel/.phan/config.php b/src/Instrumentation/Laravel/.phan/config.php index da2ac2d9..6473a9aa 100644 --- a/src/Instrumentation/Laravel/.phan/config.php +++ b/src/Instrumentation/Laravel/.phan/config.php @@ -42,7 +42,7 @@ // // Note that the **only** effect of choosing `'5.6'` is to infer that functions removed in php 7.0 exist. // (See `backward_compatibility_checks` for additional options) - 'target_php_version' => '8.0', + 'target_php_version' => '8.1', // If enabled, missing properties will be created when // they are first seen. If false, we'll report an diff --git a/src/Instrumentation/Laravel/_register.php b/src/Instrumentation/Laravel/_register.php index c265131f..1515731d 100644 --- a/src/Instrumentation/Laravel/_register.php +++ b/src/Instrumentation/Laravel/_register.php @@ -1,18 +1,27 @@ =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.18.4", - "spatie/laravel-ignition": "*", "vimeo/psalm": "^5.0" }, "autoload": { @@ -44,11 +42,25 @@ "OpenTelemetry\\Tests\\Contrib\\Instrumentation\\Laravel\\": "tests/" } }, + "suggest": { + "ext-opentelemetry": "Required to provide auto-instrumentation hooks." + }, + "extra": { + "spi-config": { + "autoload-files": true + }, + "spi": { + "OpenTelemetry\\Config\\SDK\\Configuration\\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 0906e0e2..0af9a508 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 00000000..311b6fe3 --- /dev/null +++ b/src/Instrumentation/Laravel/src/ComponentProvider/LaravelComponentProvider.php @@ -0,0 +1,33 @@ +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 00000000..cdf41d2a --- /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); } - 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, $function) ->setAttribute(TraceAttributes::CODE_NAMESPACE, $class) @@ -45,7 +54,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 edf961f9..3c186e17 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Console/Kernel.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Console/Kernel.php @@ -6,41 +6,49 @@ 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\Version; use Throwable; -class Kernel implements LaravelHook +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); } } - 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, $function) @@ -54,7 +62,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 b5415cc2..a1a52cc2 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Http/Kernel.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Http/Kernel.php @@ -7,42 +7,54 @@ use Illuminate\Contracts\Http\Kernel as KernelContract; 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\Version; use Symfony\Component\HttpFoundation\Response; use Throwable; -class Kernel implements LaravelHook +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); } - 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, $function) @@ -52,7 +64,7 @@ protected function hookHandle(): bool $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()) @@ -75,7 +87,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; 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 c58800db..b851d360 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Queue/Queue.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Contracts/Queue/Queue.php @@ -7,36 +7,46 @@ 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\Contrib\Instrumentation\Laravel\LaravelInstrumentation; use OpenTelemetry\SemConv\TraceAttributes; use OpenTelemetry\SemConv\TraceAttributeValues; +use OpenTelemetry\SemConv\Version; use Throwable; -class Queue implements LaravelHook +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); } - 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 => $function, TraceAttributes::CODE_NAMESPACE => $class, @@ -46,12 +56,11 @@ protected function hookBulk(): bool ], $this->contextualMessageSystemAttributes($queue, [])); /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation - ->tracer() + $span = $tracer ->spanBuilder(vsprintf('%s %s', [ /** @phan-suppress-next-line PhanUndeclaredMethod */ method_exists($queue, 'getQueue') ? $queue->getQueue($params[2] ?? null) : $queue->getConnectionName(), - TraceAttributeValues::MESSAGING_OPERATION_PUBLISH, + TraceAttributeValues::MESSAGING_OPERATION_TYPE_PUBLISH, ])) ->setSpanKind(SpanKind::KIND_PRODUCER) ->setAttributes($attributes) @@ -61,18 +70,18 @@ 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); }, ); } - 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(), @@ -89,8 +98,7 @@ protected function hookLater(): bool ]; /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation - ->tracer() + $span = $tracer ->spanBuilder(vsprintf('%s %s', [ /** @phan-suppress-next-line PhanUndeclaredMethod */ method_exists($queue, 'getQueue') ? $queue->getQueue($params[2] ?? null) : $queue->getConnectionName(), @@ -104,28 +112,27 @@ 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); }, ); } - 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', [ $attributes[TraceAttributes::MESSAGING_DESTINATION_NAME], - TraceAttributeValues::MESSAGING_OPERATION_CREATE, + TraceAttributeValues::MESSAGING_OPERATION_TYPE_CREATE, ])) ->setSpanKind(SpanKind::KIND_PRODUCER) ->setAttributes($attributes) @@ -135,7 +142,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/Foundation/Application.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Application.php index 54aa70d8..2556273e 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,25 +17,36 @@ 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 +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 - { - 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 190113d8..9501bc55 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Console/ServeCommand.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Console/ServeCommand.php @@ -5,27 +5,25 @@ 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. */ -class ServeCommand implements LaravelHook +class ServeCommand implements Hook { - use LaravelHookTrait; - - public function instrument(): void - { - 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/Queue.php b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Queue.php index b1a0c7aa..5a46fe4e 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Queue.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Queue.php @@ -5,28 +5,26 @@ 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 +class Queue implements Hook { use AttributesBuilder; - use LaravelHookTrait; - public function instrument(): void - { - $this->hookAbstractQueueCreatePayloadArray(); - } - - 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 19d85cc2..115e798f 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/SyncQueue.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/SyncQueue.php @@ -5,35 +5,44 @@ 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\Contrib\Instrumentation\Laravel\LaravelInstrumentation; use OpenTelemetry\SemConv\TraceAttributes; +use OpenTelemetry\SemConv\Version; use Throwable; -class SyncQueue implements LaravelHook +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); } - 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', @@ -49,7 +58,7 @@ protected function hookPush(): bool 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 0e295a69..ca2f31fc 100644 --- a/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Worker.php +++ b/src/Instrumentation/Laravel/src/Hooks/Illuminate/Queue/Worker.php @@ -6,36 +6,46 @@ 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\Contrib\Instrumentation\Laravel\LaravelInstrumentation; use OpenTelemetry\SemConv\TraceAttributes; use OpenTelemetry\SemConv\TraceAttributeValues; +use OpenTelemetry\SemConv\Version; use Throwable; -class Worker implements LaravelHook +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); } - 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]; @@ -48,8 +58,7 @@ 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', [ $attributes[TraceAttributes::MESSAGING_DESTINATION_NAME], 'process', @@ -63,7 +72,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; @@ -82,12 +91,12 @@ private function hookWorkerProcess(): bool ); } - 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]; @@ -95,11 +104,10 @@ private function hookWorkerGetNextJob(): bool $attributes = $this->buildMessageAttributes($connection, '', $queue); /** @psalm-suppress ArgumentTypeCoercion */ - $span = $this->instrumentation - ->tracer() + $span = $tracer ->spanBuilder(vsprintf('%s %s', [ $attributes[TraceAttributes::MESSAGING_DESTINATION_NAME], - TraceAttributeValues::MESSAGING_OPERATION_RECEIVE, + TraceAttributeValues::MESSAGING_OPERATION_TYPE_RECEIVE, ])) ->setSpanKind(SpanKind::KIND_CONSUMER) ->setAttributes($attributes) @@ -109,7 +117,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 83b8f080..00000000 --- a/src/Instrumentation/Laravel/src/Hooks/LaravelHook.php +++ /dev/null @@ -1,14 +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 00000000..dd331859 --- /dev/null +++ b/src/Instrumentation/Laravel/src/LaravelConfiguration.php @@ -0,0 +1,28 @@ +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 cc6b3d53..e78a2d24 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, ) { } @@ -52,15 +52,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 7adcddd9..cacb4cea 100644 --- a/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php +++ b/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php @@ -7,16 +7,17 @@ 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\Map\Psr3; +use OpenTelemetry\API\Logs\Severity; use TypeError; class LogWatcher extends Watcher { - private LogManager $logger; + private LogManager $logManager; + public function __construct( - private CachedInstrumentation $instrumentation, + private readonly LoggerInterface $logger, ) { } @@ -27,7 +28,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']; } /** @@ -35,7 +36,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. @@ -54,13 +55,11 @@ public function recordLog(MessageLogged $log): void 'context' => json_encode(array_filter($log->context)), ]; - $logger = $this->instrumentation->logger(); - $record = (new LogRecord($log->message)) ->setSeverityText($log->level) - ->setSeverityNumber(Psr3::severityNumber($log->level)) + ->setSeverityNumber(Severity::fromPsr3($log->level)) ->setAttributes($attributes); - $logger->emit($record); + $this->logger->emit($record); } } diff --git a/src/Instrumentation/Laravel/src/Watchers/QueryWatcher.php b/src/Instrumentation/Laravel/src/Watchers/QueryWatcher.php index ff1c82d5..98d3385d 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, ) { } @@ -27,8 +27,10 @@ public function register(Application $app): void /** * Record a query. + * + * @psalm-suppress UndefinedThisPropertyFetch + * @phan-suppress PhanDeprecatedClassConstant */ - /** @psalm-suppress UndefinedThisPropertyFetch */ public function recordQuery(QueryExecuted $query): void { $nowInNs = (int) (microtime(true) * 1E9); @@ -38,7 +40,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 6e798d4e..984c74a6 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, ) { } @@ -37,8 +37,10 @@ public function register(Application $app): void /** * Record a Redis command. + * + * @psalm-suppress UndefinedThisPropertyFetch + * @phan-suppress PhanDeprecatedClassConstant */ - /** @psalm-suppress UndefinedThisPropertyFetch */ public function recordRedisCommand(CommandExecuted $event): void { $nowInNs = (int) (microtime(true) * 1E9); @@ -46,7 +48,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 +84,7 @@ private function fetchDbIndex(Connection $connection): ?int } return null; - } catch (Throwable $e) { + } catch (Throwable) { return null; } } @@ -98,7 +100,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 00000000..21a41dda --- /dev/null +++ b/src/Instrumentation/Laravel/tests/Fixtures/ComponentProvider/LogRecordExporterInMemory.php @@ -0,0 +1,26 @@ +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 00000000..15a55c70 --- /dev/null +++ b/src/Instrumentation/Laravel/tests/Fixtures/otel-instrumentation.yaml @@ -0,0 +1,18 @@ +file_format: '0.3' + +logger_provider: + processors: + - simple: + exporter: + test/in_memory_exporter: {} + +tracer_provider: + processors: + - simple: + exporter: + test/in_memory_exporter: {} + +instrumentation: + php: + laravel: + enabled: true diff --git a/src/Instrumentation/Laravel/tests/Integration/TestCase.php b/src/Instrumentation/Laravel/tests/Integration/TestCase.php index 56c1abbb..c842e6cd 100644 --- a/src/Instrumentation/Laravel/tests/Integration/TestCase.php +++ b/src/Instrumentation/Laravel/tests/Integration/TestCase.php @@ -5,56 +5,24 @@ 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; 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 00000000..14dc9ac5 --- /dev/null +++ b/src/Instrumentation/Laravel/tests/bootstrap.php @@ -0,0 +1,19 @@ +