From 497fc4e43216b72deca14b3213244e25eed4108b Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Oct 2022 21:25:04 +0200 Subject: [PATCH] Move Monolog to Addons --- bin/console.php | 3 + composer.json | 2 +- composer.lock | 84 +------- doc/Addons.md | 4 + doc/de/Addons.md | 4 + src/Core/Logger/Factory/Logger.php | 179 +++++++----------- .../Logger/Type/Monolog/DevelopHandler.php | 76 -------- .../Type/Monolog/IntrospectionProcessor.php | 62 ------ src/Core/Logger/Type/README.md | 1 - src/Database/Database.php | 7 +- tests/src/Core/Cache/DatabaseCacheTest.php | 3 +- .../src/Core/Storage/DatabaseStorageTest.php | 3 +- .../Storage/Repository/StorageManagerTest.php | 2 +- 13 files changed, 92 insertions(+), 338 deletions(-) delete mode 100644 src/Core/Logger/Type/Monolog/DevelopHandler.php delete mode 100644 src/Core/Logger/Type/Monolog/IntrospectionProcessor.php diff --git a/bin/console.php b/bin/console.php index 3e588acd6d..41e0172489 100755 --- a/bin/console.php +++ b/bin/console.php @@ -26,6 +26,7 @@ if (php_sapi_name() !== 'cli') { } use Dice\Dice; +use Friendica\DI; use Psr\Log\LoggerInterface; require dirname(__DIR__) . '/vendor/autoload.php'; @@ -33,6 +34,8 @@ require dirname(__DIR__) . '/vendor/autoload.php'; $dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php'); $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['console']]); +/// @fixme Necessary until Hooks inside the Logger can get loaded without the DI-class +DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); (new Friendica\Core\Console($dice, $argv))->execute(); diff --git a/composer.json b/composer.json index cc3a0db5fd..959e8de31c 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,6 @@ "michelf/php-markdown": "^1.7", "minishlink/web-push": "^6.0", "mobiledetect/mobiledetectlib": "^2.8", - "monolog/monolog": "^1.25", "nikic/fast-route": "^1.3", "paragonie/hidden-string": "^1.0", "patrickschur/language-detection": "^5.0.0", @@ -50,6 +49,7 @@ "pragmarx/google2fa": "^5.0", "pragmarx/recovery": "^0.2", "psr/container": "^1.0", + "psr/log": "^1.1", "seld/cli-prompt": "^1.0", "smarty/smarty": "^4", "ua-parser/uap-php": "^3.9", diff --git a/composer.lock b/composer.lock index 9de7fd6966..96949561bb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f5922f03b367e68a5930df6ed80c5c2f", + "content-hash": "c851fbba46ed090d0fbaf68e9b3b94df", "packages": [ { "name": "asika/simple-console", @@ -1543,88 +1543,6 @@ ], "time": "2022-02-17T19:24:25+00:00" }, - { - "name": "monolog/monolog", - "version": "1.27.1", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "904713c5929655dc9b97288b69cfeedad610c9a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/904713c5929655dc9b97288b69cfeedad610c9a1", - "reference": "904713c5929655dc9b97288b69cfeedad610c9a1", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpstan/phpstan": "^0.12.59", - "phpunit/phpunit": "~4.5", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" - }, - "type": "library", - "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], - "time": "2022-06-09T08:53:42+00:00" - }, { "name": "nikic/fast-route", "version": "v1.3.0", diff --git a/doc/Addons.md b/doc/Addons.md index bbc0ed61c1..91765ec484 100644 --- a/doc/Addons.md +++ b/doc/Addons.md @@ -919,6 +919,10 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep- Hook::callAll('block', $hook_data); Hook::callAll('unblock', $hook_data); +### src/Core/Logger/Factory.php + + Hook::callAll('logger_instance', $data); + ### src/Core/StorageManager Hook::callAll('storage_instance', $data); diff --git a/doc/de/Addons.md b/doc/de/Addons.md index 5a4ca98a0b..342dd1b815 100644 --- a/doc/de/Addons.md +++ b/doc/de/Addons.md @@ -405,6 +405,10 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Hook::callAll('block', $hook_data); Hook::callAll('unblock', $hook_data); +### src/Core/Logger/Factory.php + + Hook::callAll('logger_instance', $data); + ### src/Core/StorageManager Hook::callAll('storage_instance', $data); diff --git a/src/Core/Logger/Factory/Logger.php b/src/Core/Logger/Factory/Logger.php index 954784c950..4df7d8ecaf 100644 --- a/src/Core/Logger/Factory/Logger.php +++ b/src/Core/Logger/Factory/Logger.php @@ -22,19 +22,16 @@ namespace Friendica\Core\Logger\Factory; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Logger\Exception\LoggerException; use Friendica\Core; use Friendica\Core\Logger\Exception\LogLevelException; use Friendica\Database\Database; +use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Util\FileSystem; use Friendica\Core\Logger\Util\Introspection; -use Friendica\Core\Logger\Type\Monolog\DevelopHandler; -use Friendica\Core\Logger\Type\Monolog\IntrospectionProcessor; use Friendica\Core\Logger\Type\ProfilerLogger; use Friendica\Core\Logger\Type\StreamLogger; use Friendica\Core\Logger\Type\SyslogLogger; use Friendica\Util\Profiler; -use Monolog; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use Psr\Log\NullLogger; @@ -60,9 +57,15 @@ class Logger /** @var string The log-channel (app, worker, ...) */ private $channel; - public function __construct(string $channel) + public function __construct(string $channel, bool $includeAddon = true) { $this->channel = $channel; + + /// @fixme clean solution = Making Addon & Hook dynamic and load them inside the constructor, so there's no custom load logic necessary anymore + if ($includeAddon) { + Core\Addon::loadAddons(); + Core\Hook::loadHooks(); + } } /** @@ -88,35 +91,9 @@ class Logger $minLevel = $minLevel ?? $config->get('system', 'loglevel'); $loglevel = self::mapLegacyConfigDebugLevel((string)$minLevel); - switch ($config->get('system', 'logger_config', 'stream')) { - case 'monolog': - $loggerTimeZone = new \DateTimeZone('UTC'); - Monolog\Logger::setTimezone($loggerTimeZone); - - $logger = new Monolog\Logger($this->channel); - $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor()); - $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor()); - $logger->pushProcessor(new Monolog\Processor\UidProcessor()); - $logger->pushProcessor(new IntrospectionProcessor($introspection, LogLevel::DEBUG)); - - $stream = $config->get('system', 'logfile'); - - // just add a stream in case it's either writable or not file - if (!is_file($stream) || is_writable($stream)) { - try { - static::addStreamHandler($logger, $stream, $loglevel); - } catch (\Throwable $e) { - // No Logger .. - try { - $logger = new SyslogLogger($this->channel, $introspection, $loglevel); - } catch (\Throwable $e) { - // No logger ... - $logger = new NullLogger(); - } - } - } - break; + $name = $config->get('system', 'logger_config', 'stream'); + switch ($name) { case 'syslog': try { $logger = new SyslogLogger($this->channel, $introspection, $loglevel, $config->get('system', 'syslog_flags', SyslogLogger::DEFAULT_FLAGS), $config->get('system', 'syslog_facility', SyslogLogger::DEFAULT_FACILITY)); @@ -132,29 +109,48 @@ class Logger case 'stream': default: - $stream = $config->get('system', 'logfile'); - // just add a stream in case it's either writable or not file - if (!is_file($stream) || is_writable($stream)) { - try { - $logger = new StreamLogger($this->channel, $stream, $introspection, $fileSystem, $loglevel); - } catch (LogLevelException $exception) { - // If there's a wrong config value for loglevel, try again with standard - $logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE); - $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]); - } catch (\Throwable $t) { - // No logger ... - $logger = new NullLogger(); - } - } else { - try { - $logger = new SyslogLogger($this->channel, $introspection, $loglevel); - } catch (LogLevelException $exception) { - // If there's a wrong config value for loglevel, try again with standard - $logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE); - $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]); - } catch (\Throwable $e) { - // No logger ... - $logger = new NullLogger(); + $data = [ + 'name' => $name, + 'channel' => $this->channel, + 'introspection' => $introspection, + 'loglevel' => $loglevel, + 'logger' => null, + ]; + try { + Core\Hook::callAll('logger_instance', $data); + } catch (InternalServerErrorException $exception) { + $data['logger'] = null; + } + + if (($data['logger'] ?? null) instanceof LoggerInterface) { + $logger = $data['logger']; + } + + if (empty($logger)) { + $stream = $config->get('system', 'logfile'); + // just add a stream in case it's either writable or not file + if (!is_file($stream) || is_writable($stream)) { + try { + $logger = new StreamLogger($this->channel, $stream, $introspection, $fileSystem, $loglevel); + } catch (LogLevelException $exception) { + // If there's a wrong config value for loglevel, try again with standard + $logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE); + $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]); + } catch (\Throwable $t) { + // No logger ... + $logger = new NullLogger(); + } + } else { + try { + $logger = new SyslogLogger($this->channel, $introspection, $loglevel); + } catch (LogLevelException $exception) { + // If there's a wrong config value for loglevel, try again with standard + $logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE); + $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]); + } catch (\Throwable $e) { + // No logger ... + $logger = new NullLogger(); + } } } break; @@ -197,27 +193,11 @@ class Logger return new NullLogger(); } - $loggerTimeZone = new \DateTimeZone('UTC'); - Monolog\Logger::setTimezone($loggerTimeZone); - $introspection = new Introspection(self::$ignoreClassList); - switch ($config->get('system', 'logger_config', 'stream')) { + $name = $config->get('system', 'logger_config', 'stream'); - case 'monolog': - $loggerTimeZone = new \DateTimeZone('UTC'); - Monolog\Logger::setTimezone($loggerTimeZone); - - $logger = new Monolog\Logger(self::DEV_CHANNEL); - $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor()); - $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor()); - $logger->pushProcessor(new Monolog\Processor\UidProcessor()); - $logger->pushProcessor(new IntrospectionProcessor($introspection, LogLevel::DEBUG)); - - $logger->pushHandler(new DevelopHandler($developerIp)); - - static::addStreamHandler($logger, $stream, LogLevel::DEBUG); - break; + switch ($name) { case 'syslog': $logger = new SyslogLogger(self::DEV_CHANNEL, $introspection, LogLevel::DEBUG); @@ -225,6 +205,23 @@ class Logger case 'stream': default: + $data = [ + 'name' => $name, + 'channel' => self::DEV_CHANNEL, + 'introspection' => $introspection, + 'loglevel' => LogLevel::DEBUG, + 'logger' => null, + ]; + try { + Core\Hook::callAll('logger_instance', $data); + } catch (InternalServerErrorException $exception) { + $data['logger'] = null; + } + + if (($data['logger'] ?? null) instanceof LoggerInterface) { + return $data['logger']; + } + $logger = new StreamLogger(self::DEV_CHANNEL, $stream, $introspection, $fileSystem, LogLevel::DEBUG); break; } @@ -273,38 +270,4 @@ class Logger return $level; } } - - /** - * Adding a handler to a given logger instance - * - * @param LoggerInterface $logger The logger instance - * @param mixed $stream The stream which handles the logger output - * @param string $level The level, for which this handler at least should handle logging - * - * @return void - * - * @throws LoggerException - */ - public static function addStreamHandler(LoggerInterface $logger, $stream, string $level = LogLevel::NOTICE) - { - if ($logger instanceof Monolog\Logger) { - $loglevel = Monolog\Logger::toMonologLevel($level); - - // fallback to notice if an invalid loglevel is set - if (!is_int($loglevel)) { - $loglevel = LogLevel::NOTICE; - } - - try { - $fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel); - - $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n"); - $fileHandler->setFormatter($formatter); - - $logger->pushHandler($fileHandler); - } catch (\Exception $exception) { - throw new LoggerException('Cannot create Monolog Logger.', $exception); - } - } - } } diff --git a/src/Core/Logger/Type/Monolog/DevelopHandler.php b/src/Core/Logger/Type/Monolog/DevelopHandler.php deleted file mode 100644 index febb8d6cda..0000000000 --- a/src/Core/Logger/Type/Monolog/DevelopHandler.php +++ /dev/null @@ -1,76 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Logger\Type\Monolog; - -use Friendica\App\Request; -use Monolog\Handler; -use Monolog\Logger; - -/** - * Simple handler for Friendica developers to use for deeper logging - * - * If you want to debug only interactions from your IP or the IP of a remote server for federation debug, - * you'll use Logger::develop() for the duration of your work, and you clean it up when you're done before submitting your PR. - */ -class DevelopHandler extends Handler\AbstractHandler -{ - /** - * @var string The IP of the developer who wants to debug - */ - private $developerIp; - - /** - * @var string The IP of the current request - */ - private $remoteAddress; - - /** - * @param Request $request The current http request - * @param string $developerIp The IP of the developer who wants to debug - * @param int $level The minimum logging level at which this handler will be triggered - * @param bool $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct(Request $request, $developerIp, int $level = Logger::DEBUG, bool $bubble = true) - { - parent::__construct($level, $bubble); - - $this->developerIp = $developerIp; - $this->remoteAddress = $request->getRemoteAddress(); - } - - /** - * {@inheritdoc} - */ - public function handle(array $record): bool - { - if (!$this->isHandling($record)) { - return false; - } - - /// Just in case the remote IP is the same as the developer IP log the output - if (!is_null($this->developerIp) && $this->remoteAddress != $this->developerIp) { - return false; - } - - return false === $this->bubble; - } -} diff --git a/src/Core/Logger/Type/Monolog/IntrospectionProcessor.php b/src/Core/Logger/Type/Monolog/IntrospectionProcessor.php deleted file mode 100644 index 99a9defaf8..0000000000 --- a/src/Core/Logger/Type/Monolog/IntrospectionProcessor.php +++ /dev/null @@ -1,62 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Logger\Type\Monolog; - -use Friendica\Core\Logger\Util\Introspection; -use Monolog\Logger; -use Monolog\Processor\ProcessorInterface; - -/** - * Injects line/file//function where the log message came from - */ -class IntrospectionProcessor implements ProcessorInterface -{ - private $level; - - private $introspection; - - /** - * @param Introspection $introspection Holds the Introspection of the current call - * @param string|int $level The minimum logging level at which this Processor will be triggered - */ - public function __construct(Introspection $introspection, $level = Logger::DEBUG) - { - $this->level = Logger::toMonologLevel($level); - $introspection->addClasses(['Monolog\\']); - $this->introspection = $introspection; - } - - public function __invoke(array $record): array - { - // return if the level is not high enough - if ($record['level'] < $this->level) { - return $record; - } - // we should have the call source now - $record['extra'] = array_merge( - $record['extra'], - $this->introspection->getRecord() - ); - - return $record; - } -} diff --git a/src/Core/Logger/Type/README.md b/src/Core/Logger/Type/README.md index 449403194d..b204353c04 100644 --- a/src/Core/Logger/Type/README.md +++ b/src/Core/Logger/Type/README.md @@ -5,7 +5,6 @@ This namespace contains the different implementations of a Logger. ### Configuration guideline The following settings are possible for `logger_config`: -- `monolog`: A Logging framework with lots of additions (see [Monolog](https://github.com/Seldaek/monolog/)). There are just Friendica additions inside the Monolog directory - [`stream`](StreamLogger.php): A small logger for files or streams - [`syslog`](SyslogLogger.php): Prints the logging output into the syslog diff --git a/src/Database/Database.php b/src/Database/Database.php index fde5bd9f10..79c39e9af7 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -36,6 +36,7 @@ use PDO; use PDOException; use PDOStatement; use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; /** * This class is for the low level database stuff that does driver specific things. @@ -80,15 +81,17 @@ class Database /** @var ViewDefinition */ protected $viewDefinition; - public function __construct(Cache $configCache, Profiler $profiler, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition, LoggerInterface $logger) + public function __construct(Cache $configCache, Profiler $profiler, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition) { // We are storing these values for being able to perform a reconnect $this->configCache = $configCache; $this->profiler = $profiler; - $this->logger = $logger; $this->dbaDefinition = $dbaDefinition; $this->viewDefinition = $viewDefinition; + // Temporary NullLogger until we can fetch the logger class from the config + $this->logger = new NullLogger(); + $this->connect(); } diff --git a/tests/src/Core/Cache/DatabaseCacheTest.php b/tests/src/Core/Cache/DatabaseCacheTest.php index 71b488bcb1..e0d47c2646 100644 --- a/tests/src/Core/Cache/DatabaseCacheTest.php +++ b/tests/src/Core/Cache/DatabaseCacheTest.php @@ -48,7 +48,6 @@ class DatabaseCacheTest extends CacheTest protected function getInstance() { - $logger = new NullLogger(); $profiler = Mockery::mock(Profiler::class); $profiler->shouldReceive('startRecording'); $profiler->shouldReceive('stopRecording'); @@ -62,7 +61,7 @@ class DatabaseCacheTest extends CacheTest $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); $viewDefinition = (new ViewDefinition($configCache->get('system', 'basepath')))->load(); - $dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition, $logger); + $dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition); $this->cache = new Cache\Type\DatabaseCache('database', $dba); return $this->cache; diff --git a/tests/src/Core/Storage/DatabaseStorageTest.php b/tests/src/Core/Storage/DatabaseStorageTest.php index 75bbc4239b..2933fab52d 100644 --- a/tests/src/Core/Storage/DatabaseStorageTest.php +++ b/tests/src/Core/Storage/DatabaseStorageTest.php @@ -47,7 +47,6 @@ class DatabaseStorageTest extends StorageTest protected function getInstance() { - $logger = new NullLogger(); $profiler = \Mockery::mock(Profiler::class); $profiler->shouldReceive('startRecording'); $profiler->shouldReceive('stopRecording'); @@ -61,7 +60,7 @@ class DatabaseStorageTest extends StorageTest $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); $viewDefinition = (new ViewDefinition($configCache->get('system', 'basepath')))->load(); - $dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition, $logger); + $dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition); return new Database($dba); } diff --git a/tests/src/Core/Storage/Repository/StorageManagerTest.php b/tests/src/Core/Storage/Repository/StorageManagerTest.php index 99dee27a63..27af7f8374 100644 --- a/tests/src/Core/Storage/Repository/StorageManagerTest.php +++ b/tests/src/Core/Storage/Repository/StorageManagerTest.php @@ -87,7 +87,7 @@ class StorageManagerTest extends DatabaseTest $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); $viewDefinition = (new ViewDefinition($configCache->get('system', 'basepath')))->load(); - $this->dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition, $this->logger); + $this->dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition); $configModel = new Repository\Config($this->dba, new Mode(Mode::DBCONFIGAVAILABLE)); $this->config = new PreloadConfig($configCache, $configModel);