diff --git a/src/AbstractTracer.php b/src/AbstractTracer.php index 547e3dd..f8c073d 100644 --- a/src/AbstractTracer.php +++ b/src/AbstractTracer.php @@ -4,8 +4,9 @@ namespace Spiral\Telemetry; -use Spiral\Core\Attribute\Proxy; +use Spiral\Core\BinderInterface; use Spiral\Core\Container; +use Spiral\Core\ContainerScope; use Spiral\Core\InvokerInterface; use Spiral\Core\ScopeInterface; @@ -17,7 +18,7 @@ abstract class AbstractTracer implements TracerInterface { public function __construct( - #[Proxy] private readonly ?ScopeInterface $scope = new Container(), + private readonly ?ScopeInterface $scope = new Container(), ) { } @@ -26,10 +27,37 @@ public function __construct( */ final protected function runScope(Span $span, callable $callback): mixed { - // TODO: Can we remove this scope? - return $this->scope->runScope([ - SpanInterface::class => $span, - TracerInterface::class => $this, - ], static fn (InvokerInterface $invoker): mixed => $invoker->invoke($callback)); + $container = ContainerScope::getContainer(); + if ($container === null) { + return $this->scope->runScope([ + SpanInterface::class => $span, + TracerInterface::class => $this, + ], static fn (InvokerInterface $invoker): mixed => $invoker->invoke($callback)); + } + + if ($container instanceof Container) { + $invoker = $container; + $binder = $container; + } else { + /** @var InvokerInterface $invoker */ + $invoker = $container->get(InvokerInterface::class); + /** @var BinderInterface $binder */ + $binder = $container->get(BinderInterface::class); + } + + try { + $prevSpan = $container->get(SpanInterface::class); + } catch (\Throwable) { + $prevSpan = null; + } + + $binder->bindSingleton(SpanInterface::class, $span); + try { + return $invoker->invoke($callback); + } finally { + $prevSpan === null + ? $binder->removeBinding(SpanInterface::class) + : $binder->bindSingleton(SpanInterface::class, $prevSpan); + } } } diff --git a/src/Bootloader/TelemetryBootloader.php b/src/Bootloader/TelemetryBootloader.php index d995cfe..2ceab13 100644 --- a/src/Bootloader/TelemetryBootloader.php +++ b/src/Bootloader/TelemetryBootloader.php @@ -14,13 +14,11 @@ use Spiral\Telemetry\Config\TelemetryConfig; use Spiral\Telemetry\ConfigTracerFactoryProvider; use Spiral\Telemetry\Exception\TracerException; -use Spiral\Telemetry\LogTracer; use Spiral\Telemetry\LogTracerFactory; -use Spiral\Telemetry\NullTracer; use Spiral\Telemetry\NullTracerFactory; use Spiral\Telemetry\TracerFactoryInterface; -use Spiral\Telemetry\TracerInterface; use Spiral\Telemetry\TracerFactoryProviderInterface; +use Spiral\Telemetry\TracerInterface; final class TelemetryBootloader extends Bootloader { @@ -35,7 +33,7 @@ final class TelemetryBootloader extends Bootloader ]; public function __construct( - private readonly ConfiguratorInterface $config + private readonly ConfiguratorInterface $config, ) { } @@ -51,7 +49,7 @@ public function registerTracer(string $name, string|TracerFactoryInterface|Autow { $this->config->modify( TelemetryConfig::CONFIG, - new Append('drivers', $name, $driver) + new Append('drivers', $name, $driver), ); } @@ -59,7 +57,7 @@ public function registerTracer(string $name, string|TracerFactoryInterface|Autow * @throws TracerException */ public function initFactory( - TracerFactoryProviderInterface $tracerProvider + TracerFactoryProviderInterface $tracerProvider, ): TracerFactoryInterface { return $tracerProvider->getTracerFactory(); } @@ -68,7 +66,7 @@ public function initFactory( * @throws TracerException */ public function getTracer( - TracerFactoryInterface $tracerFactory + TracerFactoryInterface $tracerFactory, ): TracerInterface { return $tracerFactory->make(); } @@ -83,7 +81,7 @@ private function initConfig(EnvironmentInterface $env): void 'null' => NullTracerFactory::class, 'log' => LogTracerFactory::class, ], - ] + ], ); } } diff --git a/src/LogTracerFactory.php b/src/LogTracerFactory.php index 4b63d1d..c5e3b2e 100644 --- a/src/LogTracerFactory.php +++ b/src/LogTracerFactory.php @@ -21,7 +21,7 @@ final class LogTracerFactory implements TracerFactoryInterface private readonly LoggerInterface $logger; public function __construct( - #[Proxy] private readonly ScopeInterface $scope, + private readonly ScopeInterface $scope, private readonly ClockInterface $clock, LogsInterface $logs, string $channel = self::LOG_CHANNEL diff --git a/src/NullTracerFactory.php b/src/NullTracerFactory.php index ee2f5f8..1f0fd41 100644 --- a/src/NullTracerFactory.php +++ b/src/NullTracerFactory.php @@ -15,7 +15,7 @@ final class NullTracerFactory implements TracerFactoryInterface { public function __construct( - #[Proxy] private readonly ?ScopeInterface $scope = new Container(), + private readonly ?ScopeInterface $scope = new Container(), ) { } diff --git a/tests/LogTracerFactoryTest.php b/tests/LogTracerFactoryTest.php index eb5a0a5..7052419 100644 --- a/tests/LogTracerFactoryTest.php +++ b/tests/LogTracerFactoryTest.php @@ -4,7 +4,7 @@ namespace Spiral\Tests\Telemetry; -use Mockery as m; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Spiral\Core\ScopeInterface; @@ -15,26 +15,24 @@ final class LogTracerFactoryTest extends TestCase { - use m\Adapter\Phpunit\MockeryPHPUnitIntegration; - public function testMake(): void { - $logs = m::mock(LogsInterface::class); + $logs = $this->createMock(LogsInterface::class); - $logs->shouldReceive('getLogger')->once() + $logs->expects($this->once()) + ->method('getLogger') ->with('some-channel') - ->andReturn($logger = m::mock(LoggerInterface::class)); + ->willReturn($logger = $this->createMock(LoggerInterface::class)); $factory = new LogTracerFactory( - $scope = m::mock(ScopeInterface::class), - $clock = m::mock(ClockInterface::class), + $scope = $this->createMock(ScopeInterface::class), + $clock = $this->createMock(ClockInterface::class), $logs, 'some-channel' ); - $clock->shouldReceive('now'); - $scope->shouldReceive('runScope')->once(); - $logger->shouldReceive('debug')->once(); + $clock->expects($this->any())->method('now'); + $logger->expects($this->once())->method('debug'); $this->assertInstanceOf(LogTracer::class, $tracer = $factory->make()); diff --git a/tests/Monolog/TelemetryProcessorTest.php b/tests/Monolog/TelemetryProcessorTest.php index 55bb471..30aeb7e 100644 --- a/tests/Monolog/TelemetryProcessorTest.php +++ b/tests/Monolog/TelemetryProcessorTest.php @@ -5,11 +5,13 @@ namespace Spiral\Tests\Telemetry\Monolog; use Mockery as m; +use PHPUnit\Framework\Attributes\RunClassInSeparateProcess; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Spiral\Telemetry\Monolog\TelemetryProcessor; use Spiral\Telemetry\TracerInterface; +#[RunClassInSeparateProcess] final class TelemetryProcessorTest extends TestCase { use m\Adapter\Phpunit\MockeryPHPUnitIntegration; diff --git a/tests/NullTracerFactoryTest.php b/tests/NullTracerFactoryTest.php index 1788210..ca2e7ba 100644 --- a/tests/NullTracerFactoryTest.php +++ b/tests/NullTracerFactoryTest.php @@ -5,6 +5,7 @@ namespace Spiral\Tests\Telemetry; use Mockery as m; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; use Spiral\Core\ScopeInterface; use Spiral\Telemetry\NullTracer; @@ -14,16 +15,11 @@ final class NullTracerFactoryTest extends TestCase { use m\Adapter\Phpunit\MockeryPHPUnitIntegration; + #[RunInSeparateProcess] public function testMake(): void { - $factory = new NullTracerFactory( - $scope = m::mock(ScopeInterface::class) - ); + $factory = new NullTracerFactory(m::mock(ScopeInterface::class)); - $scope->shouldReceive('runScope')->once(); - - $this->assertInstanceOf(NullTracer::class, $tracer = $factory->make()); - - $tracer->trace('foo', fn() => 'hello'); + $this->assertInstanceOf(NullTracer::class, $factory->make()); } } diff --git a/tests/NullTracerTest.php b/tests/NullTracerTest.php index 839470a..d184b95 100644 --- a/tests/NullTracerTest.php +++ b/tests/NullTracerTest.php @@ -5,7 +5,11 @@ namespace Spiral\Tests\Telemetry; use Mockery as m; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use Spiral\Core\BinderInterface; +use Spiral\Core\ContainerScope; use Spiral\Core\InvokerInterface; use Spiral\Core\ScopeInterface; use Spiral\Telemetry\NullTracer; @@ -16,7 +20,8 @@ final class NullTracerTest extends TestCase { use m\Adapter\Phpunit\MockeryPHPUnitIntegration; - public function testTrace(): void + #[RunInSeparateProcess] + public function testFallbackRunScope(): void { $tracer = new NullTracer( $scope = m::mock(ScopeInterface::class) @@ -43,4 +48,41 @@ public function testTrace(): void $tracer->trace('foo', $callable, ['foo' => 'bar']) ); } + + #[RunInSeparateProcess] + public function testWithScopedContainer(): void + { + $tracer = new NullTracer( + $scope = m::mock(ScopeInterface::class) + ); + + $invoker = m::mock(InvokerInterface::class); + $binder = m::mock(BinderInterface::class); + $container = m::mock(ContainerInterface::class); + $container->expects('get') + ->with(InvokerInterface::class) + ->andReturn($invoker); + $container->expects('get') + ->with(BinderInterface::class) + ->andReturn($binder); + + $callable = fn() => 'hello'; + + $invoker->shouldReceive('invoke') + ->once() + ->with($callable) + ->andReturn('hello'); + $binder->shouldReceive('bindSingleton') + ->once(); + $binder->shouldReceive('removeBinding') + ->with(SpanInterface::class); + $scope->shouldNotReceive('runScope'); + + ContainerScope::runScope($container, function () use ($tracer, $callable) { + $this->assertSame( + 'hello', + $tracer->trace('foo', $callable, ['foo' => 'bar']) + ); + }); + } }