Skip to content

Commit

Permalink
Add LoggerBootloader with default LoggerInterface injector.
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk committed Apr 25, 2024
1 parent c958af8 commit d99fd14
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 9 deletions.
8 changes: 5 additions & 3 deletions src/Bridge/Monolog/src/Bootloader/MonologBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use Spiral\Config\Patch\Append;
use Spiral\Core\Attribute\Singleton;
use Spiral\Core\Container;
use Spiral\Logger\LoggerInjector;
use Spiral\Logger\Bootloader\LoggerBootloader;
use Spiral\Logger\LogsInterface;
use Spiral\Monolog\Config\MonologConfig;
use Spiral\Monolog\LogFactory;
Expand All @@ -35,6 +35,10 @@ final class MonologBootloader extends Bootloader
'log.rotate' => [self::class, 'logRotate'],
];

protected const DEPENDENCIES = [
LoggerBootloader::class,
];

private const DEFAULT_FORMAT = "[%datetime%] %level_name%: %message% %context%\n";

public function __construct(
Expand Down Expand Up @@ -72,8 +76,6 @@ public function init(Container $container, FinalizerInterface $finalizer): void
'globalLevel' => Logger::DEBUG,
'handlers' => [],
]);

$container->bindInjector(LoggerInterface::class, LoggerInjector::class);
}

public function addHandler(string $channel, HandlerInterface $handler): void
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Monolog/src/LogFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function __construct(
$this->eventHandler = new EventHandler($listenerRegistry, $config->getEventLevel());
}

public function getLogger(string $channel = null): LoggerInterface
public function getLogger(?string $channel = null): LoggerInterface
{
$default = $this->config->getDefault();

Expand Down
30 changes: 30 additions & 0 deletions src/Logger/src/Bootloader/LoggerBootloader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Spiral\Logger\Bootloader;

use Psr\Log\LoggerInterface;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Core\Container;
use Spiral\Logger\Attribute\LoggerChannel;
use Spiral\Logger\LogFactory;
use Spiral\Logger\LoggerInjector;
use Spiral\Logger\LogsInterface;
use Spiral\Logger\NullLogger;

/**
* Register {@see LoggerInterface} injector with support for {@see LoggerChannel} attribute.
* Register default {@see LogsInterface} implementation that produces {@see NullLogger}.
*/
final class LoggerBootloader extends Bootloader
{
protected const SINGLETONS = [
LogsInterface::class => LogFactory::class,
];

public function init(Container $container): void
{
$container->bindInjector(LoggerInterface::class, LoggerInjector::class);
}
}
12 changes: 8 additions & 4 deletions src/Logger/src/LoggerInjector.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Spiral\Logger;

use Psr\Log\LoggerInterface;
use Spiral\Core\Attribute\Proxy;
use Spiral\Core\Container\InjectorInterface;
use Spiral\Logger\Attribute\LoggerChannel;

Expand All @@ -17,9 +16,10 @@
*/
final class LoggerInjector implements InjectorInterface
{
public const DEFAULT_CHANNEL = 'default';

public function __construct(
#[Proxy]
private readonly LogsInterface $factory
private readonly LogsInterface $factory,
) {
}

Expand All @@ -32,7 +32,11 @@ public function createInjection(
): LoggerInterface {
$channel = \is_object($context) ? $this->extractChannelAttribute($context) : null;

// always return default logger as injection
// Check that null argument is available
$channel ??= (new \ReflectionMethod($this->factory, 'getLogger'))->getParameters()[0]->allowsNull()
? null
: self::DEFAULT_CHANNEL;

return $this->factory->getLogger($channel);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Logger/src/NullLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ final class NullLogger implements LoggerInterface

public function __construct(
callable $receptor,
private string $channel
private readonly string $channel
) {
$this->receptor = $receptor(...);
}
Expand Down
87 changes: 87 additions & 0 deletions src/Logger/tests/FactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,101 @@

namespace Spiral\Tests\Logger;

use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use PHPUnit\Framework\Attributes\DoesNotPerformAssertions;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Spiral\Boot\BootloadManager\DefaultInvokerStrategy;
use Spiral\Boot\BootloadManager\Initializer;
use Spiral\Boot\BootloadManager\InitializerInterface;
use Spiral\Boot\BootloadManager\InvokerStrategyInterface;
use Spiral\Boot\BootloadManager\StrategyBasedBootloadManager;
use Spiral\Boot\Environment;
use Spiral\Boot\EnvironmentInterface;
use Spiral\Core\Container;
use Spiral\Logger\Attribute\LoggerChannel;
use Spiral\Logger\Bootloader\LoggerBootloader;
use Spiral\Logger\Event\LogEvent;
use Spiral\Logger\ListenerRegistry;
use Spiral\Logger\LogFactory;
use Spiral\Logger\LoggerInjector;
use Spiral\Logger\LogsInterface;

class FactoryTest extends TestCase
{
use MockeryPHPUnitIntegration;

protected Container $container;

protected function setUp(): void
{
$this->container = new Container();
$this->container->bind(EnvironmentInterface::class, new Environment());
$this->container->bind(InvokerStrategyInterface::class, DefaultInvokerStrategy::class);
$this->container->bind(InitializerInterface::class, Initializer::class);
}

#[DoesNotPerformAssertions]
public function testDefaultLogger(): void
{
$factory = new LogFactory(new ListenerRegistry());
$factory->getLogger('default');
}

public function testInjection(): void
{
$factory = new class () implements LogsInterface {
public function getLogger(string $channel): LoggerInterface
{
$mock = \Mockery::mock(LoggerInterface::class);
$mock->shouldReceive('getName')->andReturn($channel);
return $mock;
}
};
$this->container->get(StrategyBasedBootloadManager::class)->bootload([LoggerBootloader::class]);
$this->container->bindSingleton(LogsInterface::class, $factory);

$this->assertInstanceOf(LoggerInterface::class, $logger = $this->container->get(LoggerInterface::class));
$this->assertSame(LoggerInjector::DEFAULT_CHANNEL, $logger->getName());
}

public function testInjectionNullableChannel(): void
{
$factory = new class () implements LogsInterface {
public function getLogger(?string $channel): LoggerInterface
{
$mock = \Mockery::mock(LoggerInterface::class);
$mock->shouldReceive('getName')->andReturn($channel);
return $mock;
}
};
$this->container->get(StrategyBasedBootloadManager::class)->bootload([LoggerBootloader::class]);
$this->container->bindSingleton(LogsInterface::class, $factory);

$this->assertInstanceOf(LoggerInterface::class, $logger = $this->container->get(LoggerInterface::class));
$this->assertNull($logger->getName());
}

public function testInjectionWithAttribute(): void
{
$factory = new class () implements LogsInterface {
public function getLogger(?string $channel): LoggerInterface
{
$mock = \Mockery::mock(LoggerInterface::class);
$mock->shouldReceive('getName')->andReturn($channel);
return $mock;
}
};
$this->container->get(StrategyBasedBootloadManager::class)->bootload([LoggerBootloader::class]);
$this->container->bindSingleton(LogsInterface::class, $factory);

$this->container->invoke(function (#[LoggerChannel('foo')] LoggerInterface $logger) {
$this->assertSame('foo', $logger->getName());
});
}


public function testEvent(): void
{
$l = new ListenerRegistry();
Expand Down

0 comments on commit d99fd14

Please sign in to comment.