--- /dev/null
+<?php
+
+// Copyright (C) 2010-2024, the Friendica project
+// SPDX-FileCopyrightText: 2010-2024 the Friendica project
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+declare(strict_types=1);
+
+namespace Friendica\Core\Logger\Factory;
+
+use Friendica\Core\Config\Capability\IManageConfigValues;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+/**
+ * Delegates the creation of a logger based on config to other factories
+ */
+final class DelegatingLoggerFactory implements LoggerFactory
+{
+ private IManageConfigValues $config;
+
+ /** @var array<string,LoggerFactory> */
+ private array $factories = [];
+
+ public function __construct(IManageConfigValues $config)
+ {
+ $this->config = $config;
+ }
+
+ public function registerFactory(string $name, LoggerFactory $factory): void
+ {
+ $this->factories[$name] = $factory;
+ }
+
+ /**
+ * Creates and returns a PSR-3 Logger instance.
+ *
+ * Calling this method multiple times with the same parameters SHOULD return the same object.
+ *
+ * @param \Psr\Log\LogLevel::* $logLevel The log level
+ * @param \Friendica\Core\Logger\Capability\LogChannel::* $logChannel The log channel
+ */
+ public function createLogger(string $logLevel, string $logChannel): LoggerInterface
+ {
+ $factoryName = $this->config->get('system', 'logger_config') ?? '';
+
+ if (!array_key_exists($factoryName, $this->factories)) {
+ return new NullLogger();
+ }
+
+ $factory = $this->factories[$factoryName];
+
+ try {
+ $logger = $factory->createLogger($logLevel, $logChannel);
+ } catch (\Throwable $th) {
+ return new NullLogger();
+ }
+
+ return $logger;
+ }
+}
--- /dev/null
+<?php
+
+// Copyright (C) 2010-2024, the Friendica project
+// SPDX-FileCopyrightText: 2010-2024 the Friendica project
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+declare(strict_types=1);
+
+namespace Friendica\Test\Unit\Core\Logger\Factory;
+
+use Exception;
+use Friendica\Core\Config\Capability\IManageConfigValues;
+use Friendica\Core\Logger\Capability\LogChannel;
+use Friendica\Core\Logger\Factory\DelegatingLoggerFactory;
+use Friendica\Core\Logger\Factory\LoggerFactory;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+use Psr\Log\NullLogger;
+
+class DelegatingLoggerFactoryTest extends TestCase
+{
+ public function testCreateLoggerReturnsPsrLogger(): void
+ {
+ $config = $this->createStub(IManageConfigValues::class);
+ $config->method('get')->willReturnMap([
+ ['system', 'logger_config', null, 'test'],
+ ]);
+
+ $factory = new DelegatingLoggerFactory($config);
+
+ $factory->registerFactory('test', $this->createStub(LoggerFactory::class));
+
+ $this->assertInstanceOf(
+ LoggerInterface::class,
+ $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT)
+ );
+ }
+
+ public function testCreateLoggerWithoutRegisteredFactoryReturnsNullLogger(): void
+ {
+ $config = $this->createStub(IManageConfigValues::class);
+ $config->method('get')->willReturnMap([
+ ['system', 'logger_config', null, 'not-existing-factory'],
+ ]);
+
+ $factory = new DelegatingLoggerFactory($config);
+
+ $this->assertInstanceOf(
+ NullLogger::class,
+ $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT)
+ );
+ }
+
+ public function testCreateLoggerWithExceptionThrowingFactoryReturnsNullLogger(): void
+ {
+ $config = $this->createStub(IManageConfigValues::class);
+ $config->method('get')->willReturnMap([
+ ['system', 'logger_config', null, 'test'],
+ ]);
+
+ $factory = new DelegatingLoggerFactory($config);
+
+ $brokenFactory = $this->createStub(LoggerFactory::class);
+ $brokenFactory->method('createLogger')->willThrowException(new Exception());
+
+ $factory->registerFactory('test', $brokenFactory);
+
+ $this->assertInstanceOf(
+ NullLogger::class,
+ $factory->createLogger(LogLevel::DEBUG, LogChannel::DEFAULT)
+ );
+ }
+}