mirror of
https://github.com/friendica/friendica
synced 2025-04-26 17:50:11 +00:00
Introduce dynamic hook loading
- Dynamically load addon files - Dynamically load hooks - Rewrite Logger-logic to use new hook logic (Monolog is working again)
This commit is contained in:
parent
ff092833ae
commit
14b76e48f0
39 changed files with 1163 additions and 469 deletions
42
src/Core/Logger/Capabilities/ICheckLoggerSettings.php
Normal file
42
src/Core/Logger/Capabilities/ICheckLoggerSettings.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Capabilities;
|
||||
|
||||
/**
|
||||
* Whenever a logging specific check is necessary, use this interface to encapsulate and centralize this logic
|
||||
*/
|
||||
interface ICheckLoggerSettings
|
||||
{
|
||||
/**
|
||||
* Checks if the logfile is set and usable
|
||||
*
|
||||
* @return string|null null in case everything is ok, otherwise returns the error
|
||||
*/
|
||||
public function checkLogfile(): ?string;
|
||||
|
||||
/**
|
||||
* Checks if the debugging logfile is usable in case it is set!
|
||||
*
|
||||
* @return string|null null in case everything is ok, otherwise returns the error
|
||||
*/
|
||||
public function checkDebugLogfile(): ?string;
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
|
43
src/Core/Logger/Capabilities/LogChannel.php
Normal file
43
src/Core/Logger/Capabilities/LogChannel.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Capabilities;
|
||||
|
||||
/**
|
||||
* An enum class for the Log channels
|
||||
*/
|
||||
interface LogChannel
|
||||
{
|
||||
/** @var string channel for the auth_ejabbered script */
|
||||
public const AUTH_JABBERED = 'auth_ejabberd';
|
||||
/** @var string Default channel in case it isn't set explicit */
|
||||
public const DEFAULT = self::APP;
|
||||
/** @var string channel for console execution */
|
||||
public const CONSOLE = 'console';
|
||||
/** @var string channel for developer focused logging */
|
||||
public const DEV = 'dev';
|
||||
/** @var string channel for daemon executions */
|
||||
public const DAEMON = 'daemon';
|
||||
/** @var string channel for worker execution */
|
||||
public const WORKER = 'worker';
|
||||
/** @var string channel for frontend app executions */
|
||||
public const APP = 'app';
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
@ -23,6 +23,9 @@ namespace Friendica\Core\Logger\Exception;
|
|||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Exception in case the loglevel isn't set or isn't valid
|
||||
*/
|
||||
class LogLevelException extends \InvalidArgumentException
|
||||
{
|
||||
public function __construct($message = "", Throwable $previous = null)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
@ -23,6 +23,9 @@ namespace Friendica\Core\Logger\Exception;
|
|||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Exception in case an argument of a logger class isn't valid
|
||||
*/
|
||||
class LoggerArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
public function __construct($message = "", Throwable $previous = null)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
@ -23,6 +23,9 @@ namespace Friendica\Core\Logger\Exception;
|
|||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* A generic exception of the logging namespace
|
||||
*/
|
||||
class LoggerException extends \Exception
|
||||
{
|
||||
public function __construct($message = "", Throwable $previous = null)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
@ -23,7 +23,10 @@ namespace Friendica\Core\Logger\Exception;
|
|||
|
||||
use Throwable;
|
||||
|
||||
class LoggerInvalidException extends \RuntimeException
|
||||
/**
|
||||
* Exception in case the used logging instance is unusable because of some circumstances
|
||||
*/
|
||||
class LoggerUnusableException extends \RuntimeException
|
||||
{
|
||||
public function __construct($message = "", Throwable $previous = null)
|
||||
{
|
80
src/Core/Logger/Factory/AbstractLoggerTypeFactory.php
Normal file
80
src/Core/Logger/Factory/AbstractLoggerTypeFactory.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
/**
|
||||
* Abstract class for creating logger types, which includes common necessary logic/content
|
||||
*/
|
||||
abstract class AbstractLoggerTypeFactory
|
||||
{
|
||||
/** @var string */
|
||||
protected $channel;
|
||||
/** @var IHaveCallIntrospections */
|
||||
protected $introspection;
|
||||
|
||||
/**
|
||||
* @param string $channel The channel for the logger
|
||||
*/
|
||||
public function __construct(IHaveCallIntrospections $introspection, string $channel)
|
||||
{
|
||||
$this->channel = $channel;
|
||||
$this->introspection = $introspection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping a legacy level to the PSR-3 compliant levels
|
||||
*
|
||||
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel
|
||||
*
|
||||
* @param string $level the level to be mapped
|
||||
*
|
||||
* @return string the PSR-3 compliant level
|
||||
*/
|
||||
protected static function mapLegacyConfigDebugLevel(string $level): string
|
||||
{
|
||||
switch ($level) {
|
||||
// legacy WARNING
|
||||
case "0":
|
||||
return LogLevel::ERROR;
|
||||
// legacy INFO
|
||||
case "1":
|
||||
return LogLevel::WARNING;
|
||||
// legacy TRACE
|
||||
case "2":
|
||||
return LogLevel::NOTICE;
|
||||
// legacy DEBUG
|
||||
case "3":
|
||||
return LogLevel::INFO;
|
||||
// legacy DATA
|
||||
case "4":
|
||||
// legacy ALL
|
||||
case "5":
|
||||
return LogLevel::DEBUG;
|
||||
// default if nothing set
|
||||
default:
|
||||
return $level;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,134 +22,38 @@
|
|||
namespace Friendica\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capabilities\ICanManageInstances;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Friendica\Core\Logger\Type\ProfilerLogger;
|
||||
use Friendica\Core\Logger\Type\StreamLogger;
|
||||
use Friendica\Core\Logger\Type\SyslogLogger;
|
||||
use Friendica\Core\Hooks\Capabilities\ICanCreateInstances;
|
||||
use Friendica\Core\Logger\Capabilities\LogChannel;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use Psr\Log\NullLogger;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* A logger factory
|
||||
* The logger factory for the core logging instances
|
||||
*/
|
||||
class Logger
|
||||
{
|
||||
const DEV_CHANNEL = 'dev';
|
||||
|
||||
/** @var string The log-channel (app, worker, ...) */
|
||||
/** @var string The channel */
|
||||
protected $channel;
|
||||
/** @var ICanManageInstances */
|
||||
protected $instanceManager;
|
||||
/** @var IManageConfigValues */
|
||||
protected $config;
|
||||
|
||||
public function __construct(string $channel, ICanManageInstances $instanceManager, IManageConfigValues $config, string $logfile = null)
|
||||
public function __construct(string $channel = LogChannel::DEFAULT)
|
||||
{
|
||||
$this->channel = $channel;
|
||||
$this->instanceManager = $instanceManager;
|
||||
$this->config = $config;
|
||||
|
||||
$this->instanceManager
|
||||
->registerStrategy(LoggerInterface::class, 'syslog', SyslogLogger::class)
|
||||
->registerStrategy(LoggerInterface::class, 'stream', StreamLogger::class, isset($logfile) ? [$logfile] : null);
|
||||
|
||||
if ($this->config->get('system', 'profiling') ?? false) {
|
||||
$this->instanceManager->registerDecorator(LoggerInterface::class, ProfilerLogger::class);
|
||||
}
|
||||
$this->channel = $channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PSR-3 compliant logger instances
|
||||
*
|
||||
* @param string|null $loglevel (optional) A given loglevel in case the loglevel in the config isn't applicable
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*/
|
||||
public function create(string $loglevel = null): LoggerInterface
|
||||
public function create(ICanCreateInstances $createInstances, IManageConfigValues $config): LoggerInterface
|
||||
{
|
||||
if (empty($this->config->get('system', 'debugging') ?? false)) {
|
||||
if (empty($config->get('system', 'debugging') ?? false)) {
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
$loglevel = $loglevel ?? static::mapLegacyConfigDebugLevel($this->config->get('system', 'loglevel'));
|
||||
$name = $this->config->get('system', 'logger_config') ?? 'stream';
|
||||
$name = $config->get('system', 'logger_config') ?? '';
|
||||
|
||||
try {
|
||||
/** @var LoggerInterface */
|
||||
return $this->instanceManager->getInstance(LoggerInterface::class, $name, [$this->channel, $loglevel]);
|
||||
} catch (LogLevelException $exception) {
|
||||
// If there's a wrong config value for loglevel, try again with standard
|
||||
$logger = $this->create(LogLevel::NOTICE);
|
||||
$logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
|
||||
return $logger;
|
||||
} catch (\Throwable $e) {
|
||||
return $createInstances->createWithName(LoggerInterface::class, $name, [$this->channel]);
|
||||
} catch (Throwable $e) {
|
||||
// No logger ...
|
||||
return new NullLogger();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PSR-3 compliant develop logger
|
||||
*
|
||||
* If you want to debug only interactions from your IP or the IP of a remote server for federation debug,
|
||||
* you'll use this logger instance for the duration of your work.
|
||||
*
|
||||
* It should never get filled during normal usage of Friendica
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createDev()
|
||||
{
|
||||
$debugging = $this->config->get('system', 'debugging');
|
||||
$stream = $this->config->get('system', 'dlogfile');
|
||||
$developerIp = $this->config->get('system', 'dlogip');
|
||||
|
||||
if ((!isset($developerIp) || !$debugging) &&
|
||||
(!is_file($stream) || is_writable($stream))) {
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
$name = $this->config->get('system', 'logger_config') ?? 'stream';
|
||||
|
||||
/** @var LoggerInterface */
|
||||
return $this->instanceManager->getInstance(LoggerInterface::class, $name, [self::DEV_CHANNEL, LogLevel::DEBUG, $stream]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping a legacy level to the PSR-3 compliant levels
|
||||
*
|
||||
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel
|
||||
*
|
||||
* @param string $level the level to be mapped
|
||||
*
|
||||
* @return string the PSR-3 compliant level
|
||||
*/
|
||||
private static function mapLegacyConfigDebugLevel(string $level): string
|
||||
{
|
||||
switch ($level) {
|
||||
// legacy WARNING
|
||||
case "0":
|
||||
return LogLevel::ERROR;
|
||||
// legacy INFO
|
||||
case "1":
|
||||
return LogLevel::WARNING;
|
||||
// legacy TRACE
|
||||
case "2":
|
||||
return LogLevel::NOTICE;
|
||||
// legacy DEBUG
|
||||
case "3":
|
||||
return LogLevel::INFO;
|
||||
// legacy DATA
|
||||
case "4":
|
||||
// legacy ALL
|
||||
case "5":
|
||||
return LogLevel::DEBUG;
|
||||
// default if nothing set
|
||||
default:
|
||||
return $level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
53
src/Core/Logger/Factory/ProfilerLogger.php
Normal file
53
src/Core/Logger/Factory/ProfilerLogger.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Logger\Type\ProfilerLogger as ProfilerLoggerClass;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* The logger factory for the ProfilerLogger
|
||||
*
|
||||
* @see ProfilerLoggerClass
|
||||
*/
|
||||
class ProfilerLogger extends AbstractLoggerTypeFactory
|
||||
{
|
||||
/**
|
||||
* Wraps a given Logger with profiling information in case profiling is enabled
|
||||
*
|
||||
* @param IManageConfigValues $config The system configuration
|
||||
* @param LoggerInterface $logger The given logger class, which should get wrapped
|
||||
* @param Profiler $profiler The profiler utility
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*/
|
||||
public function create(IManageConfigValues $config, LoggerInterface $logger, Profiler $profiler): LoggerInterface
|
||||
{
|
||||
if ($config->get('system', 'profiling') ?? false) {
|
||||
return $logger;
|
||||
} else {
|
||||
return new ProfilerLoggerClass($logger, $profiler);
|
||||
}
|
||||
}
|
||||
}
|
100
src/Core/Logger/Factory/StreamLogger.php
Normal file
100
src/Core/Logger/Factory/StreamLogger.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Logger\Capabilities\LogChannel;
|
||||
use Friendica\Core\Logger\Exception\LoggerArgumentException;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Type\StreamLogger as StreamLoggerClass;
|
||||
use Friendica\Core\Logger\Util\FileSystem;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
/**
|
||||
* The logger factory for the StreamLogger instance
|
||||
*
|
||||
* @see StreamLoggerClass
|
||||
*/
|
||||
class StreamLogger extends AbstractLoggerTypeFactory
|
||||
{
|
||||
/**
|
||||
* Creates a new PSR-3 compliant stream logger instance
|
||||
*
|
||||
* @param IManageConfigValues $config The system configuration
|
||||
* @param string|null $logfile (optional) A given logfile which should be used as stream (e.g. in case of
|
||||
* developer logging)
|
||||
* @param string|null $channel (optional) A given channel in case it is different from the default
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*
|
||||
* @throws LoggerException in case the logger cannot get created
|
||||
*/
|
||||
public function create(IManageConfigValues $config, string $logfile = null, string $channel = null): LoggerInterface
|
||||
{
|
||||
$fileSystem = new FileSystem();
|
||||
|
||||
$logfile = $logfile ?? $config->get('system', 'logfile');
|
||||
if ((@file_exists($logfile) && !@is_writable($logfile)) && !@is_writable(basename($logfile))) {
|
||||
throw new LoggerArgumentException(sprintf('%s is not a valid logfile', $logfile));
|
||||
}
|
||||
|
||||
$loglevel = static::mapLegacyConfigDebugLevel($config->get('system', 'loglevel'));
|
||||
|
||||
if (array_key_exists($loglevel, StreamLoggerClass::levelToInt)) {
|
||||
$loglevel = StreamLoggerClass::levelToInt[$loglevel];
|
||||
} else {
|
||||
$loglevel = StreamLoggerClass::levelToInt[LogLevel::NOTICE];
|
||||
}
|
||||
|
||||
$stream = $fileSystem->createStream($logfile);
|
||||
|
||||
return new StreamLoggerClass($channel ?? $this->channel, $this->introspection, $stream, $loglevel, getmypid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PSR-3 compliant develop logger
|
||||
*
|
||||
* If you want to debug only interactions from your IP or the IP of a remote server for federation debug,
|
||||
* you'll use this logger instance for the duration of your work.
|
||||
*
|
||||
* It should never get filled during normal usage of Friendica
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*
|
||||
* @throws LoggerException
|
||||
*/
|
||||
public function createDev(IManageConfigValues $config)
|
||||
{
|
||||
$debugging = $config->get('system', 'debugging');
|
||||
$logfile = $config->get('system', 'dlogfile');
|
||||
$developerIp = $config->get('system', 'dlogip');
|
||||
|
||||
if ((!isset($developerIp) || !$debugging) &&
|
||||
(!is_file($logfile) || is_writable($logfile))) {
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
return $this->create($config, $logfile, LogChannel::DEV);
|
||||
}
|
||||
}
|
60
src/Core/Logger/Factory/SyslogLogger.php
Normal file
60
src/Core/Logger/Factory/SyslogLogger.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Type\SyslogLogger as SyslogLoggerClass;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
/**
|
||||
* The logger factory for the SyslogLogger instance
|
||||
*
|
||||
* @see SyslogLoggerClass
|
||||
*/
|
||||
class SyslogLogger extends AbstractLoggerTypeFactory
|
||||
{
|
||||
/**
|
||||
* Creates a new PSR-3 compliant syslog logger instance
|
||||
*
|
||||
* @param IManageConfigValues $config The system configuration
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*
|
||||
* @throws LoggerException in case the logger cannot get created
|
||||
*/
|
||||
public function create(IManageConfigValues $config): LoggerInterface
|
||||
{
|
||||
$logOpts = $config->get('system', 'syslog_flags') ?? SyslogLoggerClass::DEFAULT_FLAGS;
|
||||
$logFacility = $config->get('system', 'syslog_facility') ?? SyslogLoggerClass::DEFAULT_FACILITY;
|
||||
$loglevel = SyslogLogger::mapLegacyConfigDebugLevel($config->get('system', 'loglevel'));
|
||||
|
||||
if (!array_key_exists($loglevel, SyslogLoggerClass::logLevels)) {
|
||||
$loglevel = SyslogLoggerClass::logLevels[$loglevel];
|
||||
} else {
|
||||
$loglevel = SyslogLoggerClass::logLevels[LogLevel::NOTICE];
|
||||
}
|
||||
|
||||
return new SyslogLoggerClass($this->channel, $this->introspection, $loglevel, $logOpts, $logFacility);
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
## Friendica\Util\Logger
|
||||
|
||||
This namespace contains the different implementations of a Logger.
|
||||
|
||||
### Configuration guideline
|
||||
|
||||
The following settings are possible for `logger_config`:
|
||||
- [`stream`](StreamLogger.php): A small logger for files or streams
|
||||
- [`syslog`](SyslogLogger.php): Prints the logging output into the syslog
|
||||
|
||||
[`VoidLogger`](VoidLogger.php) is a fallback logger without any function if no debugging is enabled.
|
||||
|
||||
[`ProfilerLogger`](ProfilerLogger.php) is a wrapper around an existing logger in case profiling is enabled for Friendica.
|
||||
Every log call will be saved to the `Profiler` with a timestamp.
|
||||
|
||||
### Implementation guideline
|
||||
|
||||
Each logging implementation should pe capable of printing at least the following information:
|
||||
- An unique ID for each Request/Call
|
||||
- The process ID (PID)
|
||||
- A timestamp of the logging entry
|
||||
- The critically of the log entry
|
||||
- A log message
|
||||
- A context of the log message (f.e which user)
|
||||
|
||||
If possible, a Logger should extend [`AbstractLogger`](AbstractLogger.php), because it contains additional, Friendica specific business logic for each logging call.
|
|
@ -21,20 +21,16 @@
|
|||
|
||||
namespace Friendica\Core\Logger\Type;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Exception\LoggerArgumentException;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\FileSystem;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
/**
|
||||
* A Logger instance for logging into a stream (file, stdout, stderr)
|
||||
*/
|
||||
class StreamLogger extends AbstractLogger implements IAmAStrategy
|
||||
class StreamLogger extends AbstractLogger
|
||||
{
|
||||
/**
|
||||
* The minimum loglevel at which this logger will be triggered
|
||||
|
@ -42,12 +38,6 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy
|
|||
*/
|
||||
private $logLevel;
|
||||
|
||||
/**
|
||||
* The file URL of the stream (if needed)
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* The stream, where the current logger is writing into
|
||||
* @var resource
|
||||
|
@ -60,16 +50,11 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy
|
|||
*/
|
||||
private $pid;
|
||||
|
||||
/**
|
||||
* @var FileSystem
|
||||
*/
|
||||
private $fileSystem;
|
||||
|
||||
/**
|
||||
* Translates LogLevel log levels to integer values
|
||||
* @var array
|
||||
*/
|
||||
private $levelToInt = [
|
||||
public const levelToInt = [
|
||||
LogLevel::EMERGENCY => 0,
|
||||
LogLevel::ALERT => 1,
|
||||
LogLevel::CRITICAL => 2,
|
||||
|
@ -84,41 +69,20 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy
|
|||
* {@inheritdoc}
|
||||
* @param string $level The minimum loglevel at which this logger will be triggered
|
||||
*
|
||||
* @throws LoggerArgumentException
|
||||
* @throws LogLevelException
|
||||
* @throws LoggerException
|
||||
*/
|
||||
public function __construct(string $channel, IManageConfigValues $config, IHaveCallIntrospections $introspection, FileSystem $fileSystem, string $level = LogLevel::DEBUG)
|
||||
public function __construct(string $channel, IHaveCallIntrospections $introspection, $stream, int $logLevel, int $pid)
|
||||
{
|
||||
$this->fileSystem = $fileSystem;
|
||||
|
||||
$stream = $this->logfile ?? $config->get('system', 'logfile');
|
||||
if ((@file_exists($stream) && !@is_writable($stream)) && !@is_writable(basename($stream))) {
|
||||
throw new LoggerArgumentException(sprintf('%s is not a valid logfile', $stream));
|
||||
}
|
||||
|
||||
parent::__construct($channel, $introspection);
|
||||
|
||||
if (is_resource($stream)) {
|
||||
$this->stream = $stream;
|
||||
} elseif (is_string($stream)) {
|
||||
$this->url = $stream;
|
||||
} else {
|
||||
throw new LoggerArgumentException('A stream must either be a resource or a string.');
|
||||
}
|
||||
|
||||
$this->pid = getmypid();
|
||||
if (array_key_exists($level, $this->levelToInt)) {
|
||||
$this->logLevel = $this->levelToInt[$level];
|
||||
} else {
|
||||
throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
|
||||
}
|
||||
|
||||
$this->checkStream();
|
||||
$this->stream = $stream;
|
||||
$this->pid = $pid;
|
||||
$this->logLevel = $logLevel;
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if ($this->url && is_resource($this->stream)) {
|
||||
if (is_resource($this->stream)) {
|
||||
fclose($this->stream);
|
||||
}
|
||||
|
||||
|
@ -139,18 +103,16 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy
|
|||
*/
|
||||
protected function addEntry($level, string $message, array $context = [])
|
||||
{
|
||||
if (!array_key_exists($level, $this->levelToInt)) {
|
||||
if (!array_key_exists($level, static::levelToInt)) {
|
||||
throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
|
||||
}
|
||||
|
||||
$logLevel = $this->levelToInt[$level];
|
||||
$logLevel = static::levelToInt[$level];
|
||||
|
||||
if ($logLevel > $this->logLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->checkStream();
|
||||
|
||||
$formattedLog = $this->formatLog($level, $message, $context);
|
||||
fwrite($this->stream, $formattedLog);
|
||||
}
|
||||
|
@ -185,27 +147,4 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy
|
|||
|
||||
return $logMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the current stream
|
||||
*
|
||||
* @throws LoggerException
|
||||
* @throws LoggerArgumentException
|
||||
*/
|
||||
private function checkStream()
|
||||
{
|
||||
if (is_resource($this->stream)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->url)) {
|
||||
throw new LoggerArgumentException('Missing stream URL.');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->stream = $this->fileSystem->createStream($this->url);
|
||||
} catch (\UnexpectedValueException $exception) {
|
||||
throw new LoggerException('Cannot create stream.', $exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
|
||||
namespace Friendica\Core\Logger\Type;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
|
@ -32,7 +30,7 @@ use Psr\Log\LogLevel;
|
|||
* A Logger instance for syslogging (fast, but simple)
|
||||
* @see http://php.net/manual/en/function.syslog.php
|
||||
*/
|
||||
class SyslogLogger extends AbstractLogger implements IAmAStrategy
|
||||
class SyslogLogger extends AbstractLogger
|
||||
{
|
||||
const IDENT = 'Friendica';
|
||||
|
||||
|
@ -45,7 +43,7 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy
|
|||
* Translates LogLevel log levels to syslog log priorities.
|
||||
* @var array
|
||||
*/
|
||||
private $logLevels = [
|
||||
public const logLevels = [
|
||||
LogLevel::DEBUG => LOG_DEBUG,
|
||||
LogLevel::INFO => LOG_INFO,
|
||||
LogLevel::NOTICE => LOG_NOTICE,
|
||||
|
@ -60,7 +58,7 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy
|
|||
* Translates log priorities to string outputs
|
||||
* @var array
|
||||
*/
|
||||
private $logToString = [
|
||||
protected const logToString = [
|
||||
LOG_DEBUG => 'DEBUG',
|
||||
LOG_INFO => 'INFO',
|
||||
LOG_NOTICE => 'NOTICE',
|
||||
|
@ -101,19 +99,18 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy
|
|||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param string $level The minimum loglevel at which this logger will be triggered
|
||||
*
|
||||
* @throws LogLevelException
|
||||
* @throws LoggerException
|
||||
* @param string $logLevel The minimum loglevel at which this logger will be triggered
|
||||
* @param string $logOptions
|
||||
* @param string $logFacility
|
||||
*/
|
||||
public function __construct(string $channel, IManageConfigValues $config, IHaveCallIntrospections $introspection, string $level = LogLevel::NOTICE)
|
||||
public function __construct(string $channel, IHaveCallIntrospections $introspection, string $logLevel, string $logOptions, string $logFacility)
|
||||
{
|
||||
parent::__construct($channel, $introspection);
|
||||
|
||||
$this->logOpts = $config->get('system', 'syslog_flags') ?? static::DEFAULT_FLAGS;
|
||||
$this->logFacility = $config->get('system', 'syslog_facility') ?? static::DEFAULT_FACILITY;
|
||||
$this->logLevel = $this->mapLevelToPriority($level);
|
||||
$this->introspection->addClasses([self::class]);
|
||||
$this->logOpts = $logOptions;
|
||||
$this->logFacility = $logFacility;
|
||||
$this->logLevel = $logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,11 +146,11 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy
|
|||
*/
|
||||
public function mapLevelToPriority(string $level): int
|
||||
{
|
||||
if (!array_key_exists($level, $this->logLevels)) {
|
||||
if (!array_key_exists($level, static::logLevels)) {
|
||||
throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
|
||||
}
|
||||
|
||||
return $this->logLevels[$level];
|
||||
return static::logLevels[$level];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,7 +199,7 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy
|
|||
$record = array_merge($record, ['uid' => $this->logUid]);
|
||||
|
||||
$logMessage = $this->channel . ' ';
|
||||
$logMessage .= '[' . $this->logToString[$level] . ']: ';
|
||||
$logMessage .= '[' . static::logToString[$level] . ']: ';
|
||||
$logMessage .= $this->psrInterpolate($message, $context) . ' ';
|
||||
$logMessage .= $this->jsonEncodeArray($context) . ' - ';
|
||||
$logMessage .= $this->jsonEncodeArray($record);
|
||||
|
|
102
src/Core/Logger/Util/FileSystem.php
Normal file
102
src/Core/Logger/Util/FileSystem.php
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Util;
|
||||
|
||||
/**
|
||||
* Util class for filesystem manipulation for Logger classes
|
||||
*/
|
||||
class FileSystem
|
||||
{
|
||||
/**
|
||||
* @var string a error message
|
||||
*/
|
||||
private $errorMessage;
|
||||
|
||||
/**
|
||||
* Creates a directory based on a file, which gets accessed
|
||||
*
|
||||
* @param string $file The file
|
||||
*
|
||||
* @return string The directory name (empty if no directory is found, like urls)
|
||||
*/
|
||||
public function createDir(string $file): string
|
||||
{
|
||||
$dirname = null;
|
||||
$pos = strpos($file, '://');
|
||||
|
||||
if (!$pos) {
|
||||
$dirname = realpath(dirname($file));
|
||||
}
|
||||
|
||||
if (substr($file, 0, 7) === 'file://') {
|
||||
$dirname = realpath(dirname(substr($file, 7)));
|
||||
}
|
||||
|
||||
if (isset($dirname) && !is_dir($dirname)) {
|
||||
set_error_handler([$this, 'customErrorHandler']);
|
||||
$status = mkdir($dirname, 0777, true);
|
||||
restore_error_handler();
|
||||
|
||||
if (!$status && !is_dir($dirname)) {
|
||||
throw new \UnexpectedValueException(sprintf('Directory "%s" cannot get created: ' . $this->errorMessage, $dirname));
|
||||
}
|
||||
|
||||
return $dirname;
|
||||
} elseif (isset($dirname) && is_dir($dirname)) {
|
||||
return $dirname;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a stream based on a URL (could be a local file or a real URL)
|
||||
*
|
||||
* @param string $url The file/url
|
||||
*
|
||||
* @return resource the open stream resource
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function createStream(string $url)
|
||||
{
|
||||
$directory = $this->createDir($url);
|
||||
set_error_handler([$this, 'customErrorHandler']);
|
||||
if (!empty($directory)) {
|
||||
$url = $directory . DIRECTORY_SEPARATOR . pathinfo($url, PATHINFO_BASENAME);
|
||||
}
|
||||
|
||||
$stream = fopen($url, 'ab');
|
||||
restore_error_handler();
|
||||
|
||||
if (!is_resource($stream)) {
|
||||
throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: ' . $this->errorMessage, $url));
|
||||
}
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
private function customErrorHandler($code, $msg)
|
||||
{
|
||||
$this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg);
|
||||
}
|
||||
}
|
91
src/Core/Logger/Util/LoggerSettingsCheck.php
Normal file
91
src/Core/Logger/Util/LoggerSettingsCheck.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Util;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Logger\Capabilities\ICheckLoggerSettings;
|
||||
use Friendica\Core\Logger\Exception\LoggerUnusableException;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
class LoggerSettingsCheck implements ICheckLoggerSettings
|
||||
{
|
||||
/** @var IManageConfigValues */
|
||||
protected $config;
|
||||
/** @var $fileSystem */
|
||||
protected $fileSystem;
|
||||
/** @var L10n */
|
||||
protected $l10n;
|
||||
|
||||
public function __construct(IManageConfigValues $config, FileSystem $fileSystem, L10n $l10n)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->fileSystem = $fileSystem;
|
||||
$this->l10n = $l10n;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function checkLogfile(): ?string
|
||||
{
|
||||
// Check logfile permission
|
||||
if ($this->config->get('system', 'debugging')) {
|
||||
$file = $this->config->get('system', 'logfile');
|
||||
|
||||
try {
|
||||
$stream = $this->fileSystem->createStream($file);
|
||||
|
||||
if (!isset($stream)) {
|
||||
throw new LoggerUnusableException('Stream is null.');
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
return $this->l10n->t('The logfile \'%s\' is not usable. No logging possible (error: \'%s\')', $file, $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function checkDebugLogfile(): ?string
|
||||
{
|
||||
// Check logfile permission
|
||||
if ($this->config->get('system', 'debugging')) {
|
||||
$file = $this->config->get('system', 'dlogfile');
|
||||
|
||||
if (empty($file)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$stream = $this->fileSystem->createStream($file);
|
||||
|
||||
if (!isset($stream)) {
|
||||
throw new LoggerUnusableException('Stream is null.');
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
return $this->l10n->t('The debug logfile \'%s\' is not usable. No logging possible (error: \'%s\')', $file, $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue