/
githubmirror
/
framework
Обзор
Документация
Войти
/
githubmirror
/
framework
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
13.x
tests/Integration/Log/LogManagerTest.php
905 строк
30 KB
Lucas Michot
Move Testbench-based tests to tests/Integration (#61080)
07 авг 2026, 16:42
Не верифицирован
07 авг 2026, 16:42
486d136
Код
Авторство
О чём код?
<?php namespace Illuminate\Tests\Integration\Log; use Illuminate\Log\Logger; use Illuminate\Log\LogManager; use Monolog\Formatter\HtmlFormatter; use Monolog\Formatter\LineFormatter; use Monolog\Formatter\NormalizerFormatter; use Monolog\Handler\FingersCrossedHandler; use Monolog\Handler\LogEntriesHandler; use Monolog\Handler\NewRelicHandler; use Monolog\Handler\NullHandler; use Monolog\Handler\RotatingFileHandler; use Monolog\Handler\StreamHandler; use Monolog\Handler\SyslogHandler; use Monolog\Level; use Monolog\Logger as Monolog; use Monolog\Processor\MemoryUsageProcessor; use Monolog\Processor\PsrLogMessageProcessor; use Monolog\Processor\UidProcessor; use Orchestra\Testbench\TestCase; use PHPUnit\Framework\Attributes\DataProvider; use Psr\Log\LoggerInterface; use Psr\Log\LoggerTrait; use ReflectionProperty; use RuntimeException; class LogManagerTest extends TestCase { public function testLogManagerCachesLoggerInstances() { $manager = new LogManager($this->app); $logger1 = $manager->channel('single')->getLogger(); $logger2 = $manager->channel('single')->getLogger(); $this->assertSame($logger1, $logger2); } public function testLogManagerGetDefaultDriver() { $config = $this->app['config']; $config->set('logging.default', 'single'); $manager = new LogManager($this->app); $this->assertEmpty($manager->getChannels()); //we don't specify any channel name $manager->channel(); $this->assertCount(1, $manager->getChannels()); $this->assertSame('single', $manager->getDefaultDriver()); } public function testStackChannel() { $config = $this->app['config']; $config->set('logging.channels.stack', [ 'driver' => 'stack', 'channels' => ['stderr', 'stdout'], ]); $config->set('logging.channels.stderr', [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'level' => 'notice', 'with' => [ 'stream' => 'php://stderr', 'bubble' => false, ], 'processors' => [PsrLogMessageProcessor::class], ]); $config->set('logging.channels.stdout', [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'level' => 'info', 'with' => [ 'stream' => 'php://stdout', 'bubble' => true, ], ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('stack'); $handlers = $logger->getLogger()->getHandlers(); $this->assertInstanceOf(Logger::class, $logger); $this->assertCount(2, $handlers); $this->assertInstanceOf(StreamHandler::class, $handlers[0]); $this->assertInstanceOf(PsrLogMessageProcessor::class, $logger->getLogger()->getProcessors()[2]); $this->assertInstanceOf(StreamHandler::class, $handlers[1]); $this->assertEquals(Level::Notice, $handlers[0]->getLevel()); $this->assertEquals(Level::Info, $handlers[1]->getLevel()); $this->assertFalse($handlers[0]->getBubble()); $this->assertTrue($handlers[1]->getBubble()); } public function testParsingStackChannels() { $config = $this->app['config']; $config->set('logging.channels.stack', [ 'driver' => 'stack', 'channels' => 'single, daily, stderr', ]); $manager = new LogManager($this->app); $manager->channel('stack'); $this->assertSame( ['single', 'daily', 'stderr', 'stack'], array_keys($manager->getChannels()) ); } public function testLogManagerCreatesConfiguredMonologHandler() { $config = $this->app['config']; $config->set('logging.channels.nonbubblingstream', [ 'driver' => 'monolog', 'name' => 'foobar', 'handler' => StreamHandler::class, 'level' => 'notice', 'with' => [ 'stream' => 'php://stderr', 'bubble' => false, ], ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('nonbubblingstream'); $handlers = $logger->getLogger()->getHandlers(); $this->assertInstanceOf(Logger::class, $logger); $this->assertSame('foobar', $logger->getName()); $this->assertCount(1, $handlers); $this->assertInstanceOf(StreamHandler::class, $handlers[0]); $this->assertEquals(Level::Notice, $handlers[0]->getLevel()); $this->assertFalse($handlers[0]->getBubble()); $url = new ReflectionProperty(get_class($handlers[0]), 'url'); $this->assertSame('php://stderr', $url->getValue($handlers[0])); $config->set('logging.channels.logentries', [ 'driver' => 'monolog', 'name' => 'le', 'handler' => LogEntriesHandler::class, 'with' => [ 'token' => '123456789', ], ]); $logger = $manager->channel('logentries'); $handlers = $logger->getLogger()->getHandlers(); $logToken = new ReflectionProperty(get_class($handlers[0]), 'logToken'); $this->assertInstanceOf(LogEntriesHandler::class, $handlers[0]); $this->assertSame('123456789', $logToken->getValue($handlers[0])); } public function testLogManagerCreatesMonologHandlerWithConfiguredFormatter() { $config = $this->app['config']; $config->set('logging.channels.newrelic', [ 'driver' => 'monolog', 'name' => 'nr', 'handler' => NewRelicHandler::class, 'formatter' => 'default', ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('newrelic'); $handler = $logger->getLogger()->getHandlers()[0]; $this->assertInstanceOf(NewRelicHandler::class, $handler); $this->assertInstanceOf(NormalizerFormatter::class, $handler->getFormatter()); $config->set('logging.channels.newrelic2', [ 'driver' => 'monolog', 'name' => 'nr', 'handler' => NewRelicHandler::class, 'formatter' => HtmlFormatter::class, 'formatter_with' => [ 'dateFormat' => 'Y/m/d--test', ], ]); $logger = $manager->channel('newrelic2'); $handler = $logger->getLogger()->getHandlers()[0]; $formatter = $handler->getFormatter(); $this->assertInstanceOf(NewRelicHandler::class, $handler); $this->assertInstanceOf(HtmlFormatter::class, $formatter); $dateFormat = new ReflectionProperty(get_class($formatter), 'dateFormat'); $this->assertSame('Y/m/d--test', $dateFormat->getValue($formatter)); } public function testLogManagerCreatesMonologHandlerWithProperFormatter() { $config = $this->app->make('config'); $config->set('logging.channels.null', [ 'driver' => 'monolog', 'handler' => NullHandler::class, 'formatter' => HtmlFormatter::class, ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('null'); $handler = $logger->getLogger()->getHandlers()[0]; $this->assertInstanceOf(NullHandler::class, $handler); $config->set('logging.channels.null2', [ 'driver' => 'monolog', 'handler' => NullHandler::class, ]); $logger = $manager->channel('null2'); $handler = $logger->getLogger()->getHandlers()[0]; $this->assertInstanceOf(NullHandler::class, $handler); } public function testLogManagerCreatesMonologHandlerWithProcessors() { $config = $this->app->make('config'); $config->set('logging.channels.memory', [ 'driver' => 'monolog', 'name' => 'memory', 'handler' => StreamHandler::class, 'with' => [ 'stream' => 'php://stderr', ], 'processors' => [ MemoryUsageProcessor::class, ['processor' => PsrLogMessageProcessor::class, 'with' => ['removeUsedContextFields' => true]], ], ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('memory'); $handler = $logger->getLogger()->getHandlers()[0]; $processors = $logger->getLogger()->getProcessors(); $this->assertInstanceOf(StreamHandler::class, $handler); $this->assertInstanceOf(MemoryUsageProcessor::class, $processors[1]); $this->assertInstanceOf(PsrLogMessageProcessor::class, $processors[2]); $removeUsedContextFields = new ReflectionProperty(get_class($processors[2]), 'removeUsedContextFields'); $this->assertTrue($removeUsedContextFields->getValue($processors[2])); } public function testItUtilisesTheNullDriverDuringTestsWhenNullDriverUsed() { $config = $this->app->make('config'); $config->set('logging.default', null); $config->set('logging.channels.null', [ 'driver' => 'monolog', 'handler' => NullHandler::class, ]); $manager = new class($this->app) extends LogManager { protected function createEmergencyLogger() { throw new RuntimeException('Emergency logger was created.'); } }; // In tests, this should not need to create the emergency logger... $manager->info('message'); // we should also be able to forget the null channel... $this->assertCount(1, $manager->getChannels()); $manager->forgetChannel(); $this->assertCount(0, $manager->getChannels()); // However in production we want it to fallback to the emergency logger... $this->app['env'] = 'production'; try { $manager->info('message'); $this->fail('Emergency logger was not created as expected.'); } catch (RuntimeException $exception) { $this->assertSame('Emergency logger was created.', $exception->getMessage()); } } public function testLogManagerCreateSingleDriverWithConfiguredFormatter() { $config = $this->app['config']; $config->set('logging.channels.defaultsingle', [ 'driver' => 'single', 'name' => 'ds', 'path' => storage_path('logs/laravel.log'), 'replace_placeholders' => true, ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('defaultsingle'); $handler = $logger->getLogger()->getHandlers()[0]; $formatter = $handler->getFormatter(); $this->assertInstanceOf(StreamHandler::class, $handler); $this->assertInstanceOf(LineFormatter::class, $formatter); $this->assertInstanceOf(PsrLogMessageProcessor::class, $logger->getLogger()->getProcessors()[1]); $config->set('logging.channels.formattedsingle', [ 'driver' => 'single', 'name' => 'fs', 'path' => storage_path('logs/laravel.log'), 'formatter' => HtmlFormatter::class, 'formatter_with' => [ 'dateFormat' => 'Y/m/d--test', ], 'replace_placeholders' => false, ]); $logger = $manager->channel('formattedsingle'); $handler = $logger->getLogger()->getHandlers()[0]; $formatter = $handler->getFormatter(); $this->assertInstanceOf(StreamHandler::class, $handler); $this->assertInstanceOf(HtmlFormatter::class, $formatter); $this->assertCount(1, $logger->getLogger()->getProcessors()); $dateFormat = new ReflectionProperty(get_class($formatter), 'dateFormat'); $this->assertSame('Y/m/d--test', $dateFormat->getValue($formatter)); } public function testLogManagerCreateDailyDriverWithConfiguredFormatter() { $config = $this->app['config']; $config->set('logging.channels.defaultdaily', [ 'driver' => 'daily', 'name' => 'dd', 'path' => storage_path('logs/laravel.log'), 'replace_placeholders' => true, ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('defaultdaily'); $handler = $logger->getLogger()->getHandlers()[0]; $formatter = $handler->getFormatter(); $this->assertInstanceOf(RotatingFileHandler::class, $handler); $this->assertInstanceOf(LineFormatter::class, $formatter); $this->assertInstanceOf(PsrLogMessageProcessor::class, $logger->getLogger()->getProcessors()[1]); $config->set('logging.channels.formatteddaily', [ 'driver' => 'daily', 'name' => 'fd', 'path' => storage_path('logs/laravel.log'), 'formatter' => HtmlFormatter::class, 'formatter_with' => [ 'dateFormat' => 'Y/m/d--test', ], 'replace_placeholders' => false, ]); $logger = $manager->channel('formatteddaily'); $handler = $logger->getLogger()->getHandlers()[0]; $formatter = $handler->getFormatter(); $this->assertInstanceOf(RotatingFileHandler::class, $handler); $this->assertInstanceOf(HtmlFormatter::class, $formatter); $this->assertCount(1, $logger->getLogger()->getProcessors()); $dateFormat = new ReflectionProperty(get_class($formatter), 'dateFormat'); $this->assertSame('Y/m/d--test', $dateFormat->getValue($formatter)); } public static function rotatingFileDriverDataProvider() { return [ 'daily' => ['daily', 'Y-m-d', ['2026-01-01', '2026-01-02', '2026-01-03']], 'monthly' => ['monthly', 'Y-m', ['2026-01', '2026-02', '2026-03']], ]; } #[DataProvider('rotatingFileDriverDataProvider')] public function testRotatingFileDriversLogToADateStampedFileAndPruneOldOnes($driver, $dateFormat, $staleDates) { foreach (glob(storage_path("logs/rotating-$driver-*.log")) as $file) { unlink($file); } $stalePaths = array_map(fn ($date) => storage_path("logs/rotating-$driver-$date.log"), $staleDates); foreach ($stalePaths as $stalePath) { file_put_contents($stalePath, 'stale'); } $this->app['config']->set("logging.channels.rotating-$driver", [ 'driver' => $driver, 'path' => storage_path("logs/rotating-$driver.log"), 'level' => 'warning', 'max_files' => 3, ]); $logger = (new LogManager($this->app))->channel("rotating-$driver"); $logger->warning('Something went wrong'); $handler = $logger->getLogger()->getHandlers()[0]; $handler->close(); $expectedPath = storage_path("logs/rotating-$driver-".date($dateFormat).'.log'); $this->assertInstanceOf(RotatingFileHandler::class, $handler); $this->assertSame(Level::Warning, $handler->getLevel()); $this->assertFileExists($expectedPath); $this->assertSame($expectedPath, $handler->getUrl()); $this->assertStringContainsString('WARNING: Something went wrong', file_get_contents($expectedPath)); // max_files = 3, the oldest 4th file was deleted $this->assertFileDoesNotExist($stalePaths[0]); $this->assertFileExists($stalePaths[1]); $this->assertFileExists($stalePaths[2]); unlink($expectedPath); unlink($stalePaths[1]); unlink($stalePaths[2]); } public function testLogManagerCreateSyslogDriverWithConfiguredFormatter() { $config = $this->app['config']; $config->set('logging.channels.defaultsyslog', [ 'driver' => 'syslog', 'name' => 'ds', 'replace_placeholders' => true, ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('defaultsyslog'); $handler = $logger->getLogger()->getHandlers()[0]; $formatter = $handler->getFormatter(); $this->assertInstanceOf(SyslogHandler::class, $handler); $this->assertInstanceOf(LineFormatter::class, $formatter); $this->assertInstanceOf(PsrLogMessageProcessor::class, $logger->getLogger()->getProcessors()[1]); $config->set('logging.channels.formattedsyslog', [ 'driver' => 'syslog', 'name' => 'fs', 'formatter' => HtmlFormatter::class, 'formatter_with' => [ 'dateFormat' => 'Y/m/d--test', ], 'replace_placeholders' => false, ]); $logger = $manager->channel('formattedsyslog'); $handler = $logger->getLogger()->getHandlers()[0]; $formatter = $handler->getFormatter(); $this->assertInstanceOf(SyslogHandler::class, $handler); $this->assertInstanceOf(HtmlFormatter::class, $formatter); $this->assertCount(1, $logger->getLogger()->getProcessors()); $dateFormat = new ReflectionProperty(get_class($formatter), 'dateFormat'); $this->assertSame('Y/m/d--test', $dateFormat->getValue($formatter)); } public function testLogManagerPurgeResolvedChannels() { $manager = new LogManager($this->app); $this->assertEmpty($manager->getChannels()); $manager->channel('single')->getLogger(); $this->assertCount(1, $manager->getChannels()); $manager->forgetChannel('single'); $this->assertEmpty($manager->getChannels()); } public function testLogManagerCanBuildOnDemandChannel() { $manager = new LogManager($this->app); $logger = $manager->build([ 'driver' => 'single', 'path' => storage_path('logs/on-demand.log'), ]); $handler = $logger->getLogger()->getHandlers()[0]; $this->assertInstanceOf(StreamHandler::class, $handler); $url = new ReflectionProperty(get_class($handler), 'url'); $this->assertSame(storage_path('logs/on-demand.log'), $url->getValue($handler)); } public function testLogManagerCanUseOnDemandChannelInOnDemandStack() { $manager = new LogManager($this->app); $this->app['config']->set('logging.channels.test', [ 'driver' => 'single', ]); $factory = new class() { public function __invoke() { return new Monolog( 'uuid', [new StreamHandler(storage_path('logs/custom.log'))], [new UidProcessor()] ); } }; $channel = $manager->build([ 'driver' => 'custom', 'via' => get_class($factory), ]); $logger = $manager->stack(['test', $channel]); $handler = $logger->getLogger()->getHandlers()[1]; $processor = $logger->getLogger()->getProcessors()[1]; $this->assertInstanceOf(StreamHandler::class, $handler); $this->assertInstanceOf(UidProcessor::class, $processor); $url = new ReflectionProperty(get_class($handler), 'url'); $this->assertSame(storage_path('logs/custom.log'), $url->getValue($handler)); } public function testLogManagerCanSetChannelNameForOnDemandStack() { $manager = new LogManager($this->app); $logger = $manager->stack(['single'], 'custom'); $this->assertSame('custom', $logger->getName()); } public function testWrappingHandlerInFingersCrossedWhenActionLevelIsUsed() { $config = $this->app['config']; $config->set('logging.channels.fingerscrossed', [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'level' => 'debug', 'action_level' => 'critical', 'with' => [ 'stream' => 'php://stderr', 'bubble' => false, ], ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('fingerscrossed'); $handlers = $logger->getLogger()->getHandlers(); $this->assertInstanceOf(Logger::class, $logger); $this->assertCount(1, $handlers); $expectedFingersCrossedHandler = $handlers[0]; $this->assertInstanceOf(FingersCrossedHandler::class, $expectedFingersCrossedHandler); $activationStrategyProp = new ReflectionProperty(get_class($expectedFingersCrossedHandler), 'activationStrategy'); $activationStrategyValue = $activationStrategyProp->getValue($expectedFingersCrossedHandler); $actionLevelProp = new ReflectionProperty(get_class($activationStrategyValue), 'actionLevel'); $actionLevelValue = $actionLevelProp->getValue($activationStrategyValue); $this->assertEquals(Level::Critical, $actionLevelValue); if (method_exists($expectedFingersCrossedHandler, 'getHandler')) { $expectedStreamHandler = $expectedFingersCrossedHandler->getHandler(); } else { $handlerProp = new ReflectionProperty(get_class($expectedFingersCrossedHandler), 'handler'); $expectedStreamHandler = $handlerProp->getValue($expectedFingersCrossedHandler); } $this->assertInstanceOf(StreamHandler::class, $expectedStreamHandler); $this->assertEquals(Level::Debug, $expectedStreamHandler->getLevel()); } public function testFingersCrossedHandlerStopsRecordBufferingAfterFirstFlushByDefault() { $config = $this->app['config']; $config->set('logging.channels.fingerscrossed', [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'level' => 'debug', 'action_level' => 'critical', 'with' => [ 'stream' => 'php://stderr', 'bubble' => false, ], ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('fingerscrossed'); $handlers = $logger->getLogger()->getHandlers(); $expectedFingersCrossedHandler = $handlers[0]; $stopBufferingProp = new ReflectionProperty(get_class($expectedFingersCrossedHandler), 'stopBuffering'); $stopBufferingValue = $stopBufferingProp->getValue($expectedFingersCrossedHandler); $this->assertTrue($stopBufferingValue); } public function testFingersCrossedHandlerCanBeConfiguredToResumeBufferingAfterFlushing() { $config = $this->app['config']; $config->set('logging.channels.fingerscrossed', [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'level' => 'debug', 'action_level' => 'critical', 'stop_buffering' => false, 'with' => [ 'stream' => 'php://stderr', 'bubble' => false, ], ]); $manager = new LogManager($this->app); // create logger with handler specified from configuration $logger = $manager->channel('fingerscrossed'); $handlers = $logger->getLogger()->getHandlers(); $expectedFingersCrossedHandler = $handlers[0]; $stopBufferingProp = new ReflectionProperty(get_class($expectedFingersCrossedHandler), 'stopBuffering'); $stopBufferingValue = $stopBufferingProp->getValue($expectedFingersCrossedHandler); $this->assertFalse($stopBufferingValue); } public function testItSharesContextWithAlreadyResolvedChannels() { $manager = new LogManager($this->app); $channel = $manager->channel('single'); $context = null; $channel->listen(function ($message) use (&$context) { $context = $message->context; }); $manager->shareContext([ 'invocation-id' => 'expected-id', ]); $channel->info('xxxx'); $this->assertSame(['invocation-id' => 'expected-id'], $context); } public function testItSharesContextWithFreshlyResolvedChannels() { $manager = new LogManager($this->app); $context = null; $manager->shareContext([ 'invocation-id' => 'expected-id', ]); $manager->channel('single')->listen(function ($message) use (&$context) { $context = $message->context; }); $manager->channel('single')->info('xxxx'); $this->assertSame(['invocation-id' => 'expected-id'], $context); } public function testContextCanBePubliclyAccessedByOtherLoggingSystems() { $manager = new LogManager($this->app); $context = null; $manager->shareContext([ 'invocation-id' => 'expected-id', ]); $this->assertSame(['invocation-id' => 'expected-id'], $manager->sharedContext()); } public function testItSharesContextWithStacksWhenTheyAreResolved() { $manager = new LogManager($this->app); $context = null; $manager->shareContext([ 'invocation-id' => 'expected-id', ]); $stack = $manager->stack(['single']); $stack->listen(function ($message) use (&$context) { $context = $message->context; }); $stack->info('xxxx'); $this->assertSame(['invocation-id' => 'expected-id'], $context); } public function testItMergesSharedContextRatherThanReplacing() { $manager = new LogManager($this->app); $context = null; $manager->shareContext([ 'invocation-id' => 'expected-id', ]); $manager->shareContext([ 'invocation-start' => 1651800456, ]); $manager->channel('single')->listen(function ($message) use (&$context) { $context = $message->context; }); $manager->channel('single')->info('xxxx', [ 'logged' => 'context', ]); $this->assertSame([ 'invocation-id' => 'expected-id', 'invocation-start' => 1651800456, 'logged' => 'context', ], $context); $this->assertSame([ 'invocation-id' => 'expected-id', 'invocation-start' => 1651800456, ], $manager->sharedContext()); } public function testFlushSharedContext() { $manager = new LogManager($this->app); $manager->shareContext($context = ['foo' => 'bar']); $this->assertSame($context, $manager->sharedContext()); $manager->flushSharedContext(); $this->assertEmpty($manager->sharedContext()); } public function testLogManagerCreateCustomFormatterWithTap() { $config = $this->app['config']; $config->set('logging.channels.custom', [ 'driver' => 'single', 'tap' => [CustomizeFormatter::class], ]); $manager = new LogManager($this->app); $logger = $manager->channel('custom'); $handler = $logger->getLogger()->getHandlers()[0]; $formatter = $handler->getFormatter(); $this->assertInstanceOf(LineFormatter::class, $formatter); $format = new ReflectionProperty(get_class($formatter), 'format'); $this->assertSame( '[%datetime%] %channel%.%level_name%: %message% %context% %extra%', rtrim($format->getValue($formatter))); } public function testDriverUsersPsrLoggerManagerReturnsLogger() { // Given $config = $this->app['config']; $config->set('logging.channels.spy', [ 'driver' => 'spy', ]); $manager = new LogManager($this->app); $loggerSpy = new LoggerSpy(); $manager->extend('spy', fn () => $loggerSpy); // When $logger = $manager->channel('spy'); $logger->alert('some alert'); // Then $this->assertCount(1, $loggerSpy->logs); $this->assertSame('some alert', $loggerSpy->logs[0]['message']); } public function testCustomDriverClosureBoundObjectIsLogManager() { $config = $this->app['config']; $config->set('logging.channels.'.__CLASS__, [ 'driver' => __CLASS__, ]); $manager = new LogManager($this->app); $manager->extend(__CLASS__, fn () => $this); $this->assertSame($manager, $manager->channel(__CLASS__)->getLogger()); } public function testLogManagerCanResolveBackedEnumChannel() { $manager = new LogManager($this->app); $logger1 = $manager->channel(LogChannelName::Single); $logger2 = $manager->channel('single'); $this->assertSame($logger1, $logger2); } public function testLogManagerCanResolveBackedEnumDriver() { $manager = new LogManager($this->app); $logger1 = $manager->driver(LogChannelName::Single); $logger2 = $manager->driver('single'); $this->assertSame($logger1, $logger2); } public function testSetDefaultDriverAcceptsBackedEnum() { $manager = new LogManager($this->app); $manager->setDefaultDriver(LogChannelName::Single); $this->assertSame('single', $this->app['config']['logging.default']); } public function testForgetChannelAcceptsBackedEnum(): void { $manager = new LogManager($this->app); $logger = $manager->channel(LogChannelName::Single); $this->assertSame($logger, $manager->channel('single')); $manager->forgetChannel(LogChannelName::Single); $this->assertNotSame($logger, $manager->channel('single')); } } class CustomizeFormatter { public function __invoke($logger) { foreach ($logger->getHandlers() as $handler) { $handler->setFormatter(new LineFormatter( '[%datetime%] %channel%.%level_name%: %message% %context% %extra%' )); } } } class LoggerSpy implements LoggerInterface { use LoggerTrait; public array $logs = []; public function log($level, \Stringable|string $message, array $context = []): void { $this->logs[] = [ 'level' => $level, 'message' => $message, 'context' => $context, ]; } } enum LogChannelName: string { case Single = 'single'; }