Merge remote-tracking branch 'upstream/develop' into error-handling

This commit is contained in:
Michael 2021-10-31 05:25:39 +00:00
commit 516018861e
235 changed files with 10885 additions and 10716 deletions

View file

@ -1,368 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util;
use Exception;
use Friendica\Core\Addon;
use Friendica\Core\Config\Cache;
/**
* The ConfigFileLoader loads config-files and stores them in a ConfigCache ( @see Cache )
*
* It is capable of loading the following config files:
* - *.config.php (current)
* - *.ini.php (deprecated)
* - *.htconfig.php (deprecated)
*/
class ConfigFileLoader
{
/**
* The default name of the user defined ini file
*
* @var string
*/
const CONFIG_INI = 'local';
/**
* The default name of the user defined legacy config file
*
* @var string
*/
const CONFIG_HTCONFIG = 'htconfig';
/**
* The sample string inside the configs, which shouldn't get loaded
*
* @var string
*/
const SAMPLE_END = '-sample';
/**
* @var string
*/
private $baseDir;
/**
* @var string
*/
private $configDir;
/**
* @var string
*/
private $staticDir;
/**
* @param string $baseDir The base
* @param string $configDir
* @param string $staticDir
*/
public function __construct(string $baseDir, string $configDir, string $staticDir)
{
$this->baseDir = $baseDir;
$this->configDir = $configDir;
$this->staticDir = $staticDir;
}
/**
* Load the configuration files into an configuration cache
*
* First loads the default value for all the configuration keys, then the legacy configuration files, then the
* expected local.config.php
*
* @param Cache $config The config cache to load to
* @param array $server The $_SERVER array
* @param bool $raw Setup the raw config format
*
* @throws Exception
*/
public function setupCache(Cache $config, array $server = [], bool $raw = false)
{
// Load static config files first, the order is important
$config->load($this->loadStaticConfig('defaults'), Cache::SOURCE_FILE);
$config->load($this->loadStaticConfig('settings'), Cache::SOURCE_FILE);
// try to load the legacy config first
$config->load($this->loadLegacyConfig('htpreconfig'), Cache::SOURCE_FILE);
$config->load($this->loadLegacyConfig('htconfig'), Cache::SOURCE_FILE);
// Now load every other config you find inside the 'config/' directory
$this->loadCoreConfig($config);
$config->load($this->loadEnvConfig($server), Cache::SOURCE_ENV);
// In case of install mode, add the found basepath (because there isn't a basepath set yet
if (!$raw && empty($config->get('system', 'basepath'))) {
// Setting at least the basepath we know
$config->set('system', 'basepath', $this->baseDir, Cache::SOURCE_FILE);
}
}
/**
* Tries to load the static core-configuration and returns the config array.
*
* @param string $name The name of the configuration
*
* @return array The config array (empty if no config found)
*
* @throws Exception if the configuration file isn't readable
*/
private function loadStaticConfig($name)
{
$configName = $this->staticDir . DIRECTORY_SEPARATOR . $name . '.config.php';
$iniName = $this->staticDir . DIRECTORY_SEPARATOR . $name . '.ini.php';
if (file_exists($configName)) {
return $this->loadConfigFile($configName);
} elseif (file_exists($iniName)) {
return $this->loadINIConfigFile($iniName);
} else {
return [];
}
}
/**
* Tries to load the specified core-configuration into the config cache.
*
* @param Cache $config The Config cache
*
* @return array The config array (empty if no config found)
*
* @throws Exception if the configuration file isn't readable
*/
private function loadCoreConfig(Cache $config)
{
// try to load legacy ini-files first
foreach ($this->getConfigFiles(true) as $configFile) {
$config->load($this->loadINIConfigFile($configFile), Cache::SOURCE_FILE);
}
// try to load supported config at last to overwrite it
foreach ($this->getConfigFiles() as $configFile) {
$config->load($this->loadConfigFile($configFile), Cache::SOURCE_FILE);
}
return [];
}
/**
* Tries to load the specified addon-configuration and returns the config array.
*
* @param string $name The name of the configuration
*
* @return array The config array (empty if no config found)
*
* @throws Exception if the configuration file isn't readable
*/
public function loadAddonConfig($name)
{
$filepath = $this->baseDir . DIRECTORY_SEPARATOR . // /var/www/html/
Addon::DIRECTORY . DIRECTORY_SEPARATOR . // addon/
$name . DIRECTORY_SEPARATOR . // openstreetmap/
'config'. DIRECTORY_SEPARATOR . // config/
$name . ".config.php"; // openstreetmap.config.php
if (file_exists($filepath)) {
return $this->loadConfigFile($filepath);
} else {
return [];
}
}
/**
* Tries to load environment specific variables, based on the `env.config.php` mapping table
*
* @param array $server The $_SERVER variable
*
* @return array The config array (empty if no config was found)
*
* @throws Exception if the configuration file isn't readable
*/
public function loadEnvConfig(array $server)
{
$filepath = $this->staticDir . DIRECTORY_SEPARATOR . // /var/www/html/static/
"env.config.php"; // env.config.php
if (!file_exists($filepath)) {
return [];
}
$envConfig = $this->loadConfigFile($filepath);
$return = [];
foreach ($envConfig as $envKey => $configStructure) {
if (isset($server[$envKey])) {
$return[$configStructure[0]][$configStructure[1]] = $server[$envKey];
}
}
return $return;
}
/**
* Get the config files of the config-directory
*
* @param bool $ini True, if scan for ini-files instead of config files
*
* @return array
*/
private function getConfigFiles(bool $ini = false)
{
$files = scandir($this->configDir);
$found = array();
$filePattern = ($ini ? '*.ini.php' : '*.config.php');
// Don't load sample files
$sampleEnd = self::SAMPLE_END . ($ini ? '.ini.php' : '.config.php');
foreach ($files as $filename) {
if (fnmatch($filePattern, $filename) && substr_compare($filename, $sampleEnd, -strlen($sampleEnd))) {
$found[] = $this->configDir . '/' . $filename;
}
}
return $found;
}
/**
* Tries to load the legacy config files (.htconfig.php, .htpreconfig.php) and returns the config array.
*
* @param string $name The name of the config file (default is empty, which means .htconfig.php)
*
* @return array The configuration array (empty if no config found)
*
* @deprecated since version 2018.09
*/
private function loadLegacyConfig($name = '')
{
$name = !empty($name) ? $name : self::CONFIG_HTCONFIG;
$fullName = $this->baseDir . DIRECTORY_SEPARATOR . '.' . $name . '.php';
$config = [];
if (file_exists($fullName)) {
$a = new \stdClass();
$a->config = [];
include $fullName;
$htConfigCategories = array_keys($a->config);
// map the legacy configuration structure to the current structure
foreach ($htConfigCategories as $htConfigCategory) {
if (is_array($a->config[$htConfigCategory])) {
$keys = array_keys($a->config[$htConfigCategory]);
foreach ($keys as $key) {
$config[$htConfigCategory][$key] = $a->config[$htConfigCategory][$key];
}
} else {
$config['config'][$htConfigCategory] = $a->config[$htConfigCategory];
}
}
unset($a);
if (isset($db_host)) {
$config['database']['hostname'] = $db_host;
unset($db_host);
}
if (isset($db_user)) {
$config['database']['username'] = $db_user;
unset($db_user);
}
if (isset($db_pass)) {
$config['database']['password'] = $db_pass;
unset($db_pass);
}
if (isset($db_data)) {
$config['database']['database'] = $db_data;
unset($db_data);
}
if (isset($config['system']['db_charset'])) {
$config['database']['charset'] = $config['system']['db_charset'];
}
if (isset($pidfile)) {
$config['system']['pidfile'] = $pidfile;
unset($pidfile);
}
if (isset($default_timezone)) {
$config['system']['default_timezone'] = $default_timezone;
unset($default_timezone);
}
if (isset($lang)) {
$config['system']['language'] = $lang;
unset($lang);
}
}
return $config;
}
/**
* Tries to load the specified legacy configuration file and returns the config array.
*
* @param string $filepath
*
* @return array The configuration array
* @throws Exception
* @deprecated since version 2018.12
*/
private function loadINIConfigFile($filepath)
{
$contents = include($filepath);
$config = parse_ini_string($contents, true, INI_SCANNER_TYPED);
if ($config === false) {
throw new Exception('Error parsing INI config file ' . $filepath);
}
return $config;
}
/**
* Tries to load the specified configuration file and returns the config array.
*
* The config format is PHP array and the template for configuration files is the following:
*
* <?php return [
* 'section' => [
* 'key' => 'value',
* ],
* ];
*
* @param string $filepath The filepath of the
*
* @return array The config array0
*
* @throws Exception if the config cannot get loaded.
*/
private function loadConfigFile($filepath)
{
$config = include($filepath);
if (!is_array($config)) {
throw new Exception('Error loading config file ' . $filepath);
}
return $config;
}
}

View file

@ -23,7 +23,7 @@ namespace Friendica\Util\EMailer;
use Exception;
use Friendica\App\BaseURL;
use Friendica\Core\Config\IConfig;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n;
use Friendica\Core\Renderer;
use Friendica\Model\User;
@ -42,7 +42,7 @@ abstract class MailBuilder
/** @var L10n */
protected $l10n;
/** @var IConfig */
/** @var IManageConfigValues */
protected $config;
/** @var BaseURL */
protected $baseUrl;
@ -64,7 +64,7 @@ abstract class MailBuilder
/** @var int */
protected $recipientUid = null;
public function __construct(L10n $l10n, BaseURL $baseUrl, IConfig $config, LoggerInterface $logger)
public function __construct(L10n $l10n, BaseURL $baseUrl, IManageConfigValues $config, LoggerInterface $logger)
{
$this->l10n = $l10n;
$this->baseUrl = $baseUrl;

View file

@ -24,7 +24,7 @@ namespace Friendica\Util\EMailer;
use Exception;
use Friendica\App\BaseURL;
use Friendica\Content\Text\BBCode;
use Friendica\Core\Config\IConfig;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n;
use Friendica\Core\Renderer;
use Friendica\Network\HTTPException\InternalServerErrorException;
@ -70,7 +70,7 @@ class NotifyMailBuilder extends MailBuilder
/** @var string The item link */
private $itemLink = '';
public function __construct(L10n $l10n, BaseURL $baseUrl, IConfig $config, LoggerInterface $logger, string $siteEmailAddress, string $siteName)
public function __construct(L10n $l10n, BaseURL $baseUrl, IManageConfigValues $config, LoggerInterface $logger, string $siteEmailAddress, string $siteName)
{
parent::__construct($l10n, $baseUrl, $config, $logger);

View file

@ -24,7 +24,7 @@ namespace Friendica\Util\EMailer;
use Exception;
use Friendica\App\BaseURL;
use Friendica\Content\Text\BBCode;
use Friendica\Core\Config\IConfig;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n;
use Friendica\Core\Renderer;
use Friendica\Network\HTTPException\InternalServerErrorException;
@ -45,7 +45,7 @@ class SystemMailBuilder extends MailBuilder
/** @var string */
protected $siteAdmin;
public function __construct(L10n $l10n, BaseURL $baseUrl, IConfig $config, LoggerInterface $logger,
public function __construct(L10n $l10n, BaseURL $baseUrl, IManageConfigValues $config, LoggerInterface $logger,
string $siteEmailAddress, string $siteName)
{
parent::__construct($l10n, $baseUrl, $config, $logger);

View file

@ -22,10 +22,10 @@
namespace Friendica\Util;
use Friendica\App;
use Friendica\Core\Config\IConfig;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\Hook;
use Friendica\Core\L10n;
use Friendica\Core\PConfig\IPConfig;
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Object\EMail\IEmail;
use Friendica\Protocol\Email;
@ -38,9 +38,9 @@ use Psr\Log\LoggerInterface;
*/
class Emailer
{
/** @var IConfig */
/** @var IManageConfigValues */
private $config;
/** @var IPConfig */
/** @var IManagePersonalConfigValues */
private $pConfig;
/** @var LoggerInterface */
private $logger;
@ -54,7 +54,7 @@ class Emailer
/** @var string */
private $siteEmailName;
public function __construct(IConfig $config, IPConfig $pConfig, App\BaseURL $baseURL, LoggerInterface $logger,
public function __construct(IManageConfigValues $config, IManagePersonalConfigValues $pConfig, App\BaseURL $baseURL, LoggerInterface $logger,
L10n $defaultLang)
{
$this->config = $config;

View file

@ -73,7 +73,9 @@ class FileSystem
*
* @param string $url The file/url
*
* @return false|resource the open stream ressource
* @return resource the open stream rssource
*
* @throws \UnexpectedValueException
*/
public function createStream(string $url)
{

View file

@ -28,9 +28,9 @@ use Friendica\DI;
use Friendica\Model\APContact;
use Friendica\Model\Contact;
use Friendica\Model\User;
use Friendica\Network\CurlResult;
use Friendica\Network\HTTPClientOptions;
use Friendica\Network\IHTTPResult;
use Friendica\Network\HTTPClient\Response\CurlResult;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses;
/**
* Implements HTTP Signatures per draft-cavage-http-signatures-07.
@ -414,7 +414,7 @@ class HTTPSignature
* 'nobody' => only return the header
* 'cookiejar' => path to cookie jar file
*
* @return IHTTPResult CurlResult
* @return \Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses CurlResult
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function fetchRaw($request, $uid = 0, $opts = ['accept_content' => ['application/activity+json', 'application/ld+json']])
@ -450,7 +450,7 @@ class HTTPSignature
}
$curl_opts = $opts;
$curl_opts[HTTPClientOptions::HEADERS] = $header;
$curl_opts[HttpClientOptions::HEADERS] = $header;
if (!empty($opts['nobody'])) {
$curlResult = DI::httpClient()->head($request, $curl_opts);

View file

@ -1,107 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util;
/**
* Get Introspection information about the current call
*/
class Introspection
{
private $skipStackFramesCount;
private $skipClassesPartials;
private $skipFunctions = [
'call_user_func',
'call_user_func_array',
];
/**
* @param array $skipClassesPartials An array of classes to skip during logging
* @param int $skipStackFramesCount If the logger should use information from other hierarchy levels of the call
*/
public function __construct($skipClassesPartials = array(), $skipStackFramesCount = 0)
{
$this->skipClassesPartials = $skipClassesPartials;
$this->skipStackFramesCount = $skipStackFramesCount;
}
/**
* Adds new classes to get skipped
* @param array $classNames
*/
public function addClasses(array $classNames)
{
$this->skipClassesPartials = array_merge($this->skipClassesPartials, $classNames);
}
/**
* Returns the introspection record of the current call
*
* @return array
*/
public function getRecord()
{
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$i = 1;
while ($this->isTraceClassOrSkippedFunction($trace, $i)) {
$i++;
}
$i += $this->skipStackFramesCount;
return [
'file' => isset($trace[$i - 1]['file']) ? basename($trace[$i - 1]['file']) : null,
'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null,
'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null,
];
}
/**
* Checks if the current trace class or function has to be skipped
*
* @param array $trace The current trace array
* @param int $index The index of the current hierarchy level
*
* @return bool True if the class or function should get skipped, otherwise false
*/
private function isTraceClassOrSkippedFunction(array $trace, $index)
{
if (!isset($trace[$index])) {
return false;
}
if (isset($trace[$index]['class'])) {
foreach ($this->skipClassesPartials as $part) {
if (strpos($trace[$index]['class'], $part) !== false) {
return true;
}
}
} elseif (in_array($trace[$index]['function'], $this->skipFunctions)) {
return true;
}
return false;
}
}

View file

@ -21,7 +21,7 @@
namespace Friendica\Util;
use Friendica\Core\Cache\Duration;
use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Logger;
use Exception;
use Friendica\DI;

View file

@ -1,199 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util\Logger;
use Friendica\Util\Introspection;
use Friendica\Util\Strings;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
/**
* This class contains all necessary dependencies and calls for Friendica
* Every new Logger should extend this class and define, how addEntry() works
*
* Additional information for each Logger, who extends this class:
* - Introspection
* - UID for each call
* - Channel of the current call (i.e. index, worker, daemon, ...)
*/
abstract class AbstractLogger implements LoggerInterface
{
/**
* The output channel of this logger
* @var string
*/
protected $channel;
/**
* The Introspection for the current call
* @var Introspection
*/
protected $introspection;
/**
* The UID of the current call
* @var string
*/
protected $logUid;
/**
* Adds a new entry to the log
*
* @param int $level
* @param string $message
* @param array $context
*
* @return void
*/
abstract protected function addEntry($level, $message, $context = []);
/**
* @param string $channel The output channel
* @param Introspection $introspection The introspection of the current call
*
* @throws \Exception
*/
public function __construct($channel, Introspection $introspection)
{
$this->channel = $channel;
$this->introspection = $introspection;
$this->logUid = Strings::getRandomHex(6);
}
/**
* Simple interpolation of PSR-3 compliant replacements ( variables between '{' and '}' )
* @see https://www.php-fig.org/psr/psr-3/#12-message
*
* @param string $message
* @param array $context
*
* @return string the interpolated message
*/
protected function psrInterpolate($message, array $context = array())
{
$replace = [];
foreach ($context as $key => $value) {
// check that the value can be casted to string
if (!is_array($value) && (!is_object($value) || method_exists($value, '__toString'))) {
$replace['{' . $key . '}'] = $value;
} elseif (is_array($value)) {
$replace['{' . $key . '}'] = @json_encode($value);
}
}
return strtr($message, $replace);
}
/**
* JSON Encodes an complete array including objects with "__toString()" methods
*
* @param array $input an Input Array to encode
*
* @return false|string The json encoded output of the array
*/
protected function jsonEncodeArray(array $input)
{
$output = [];
foreach ($input as $key => $value) {
if (is_object($value) && method_exists($value, '__toString')) {
$output[$key] = $value->__toString();
} else {
$output[$key] = $value;
}
}
return @json_encode($output);
}
/**
* {@inheritdoc}
*/
public function emergency($message, array $context = array())
{
$this->addEntry(LogLevel::EMERGENCY, (string) $message, $context);
}
/**
* {@inheritdoc}
*/
public function alert($message, array $context = array())
{
$this->addEntry(LogLevel::ALERT, (string) $message, $context);
}
/**
* {@inheritdoc}
*/
public function critical($message, array $context = array())
{
$this->addEntry(LogLevel::CRITICAL, (string) $message, $context);
}
/**
* {@inheritdoc}
*/
public function error($message, array $context = array())
{
$this->addEntry(LogLevel::ERROR, (string) $message, $context);
}
/**
* {@inheritdoc}
*/
public function warning($message, array $context = array())
{
$this->addEntry(LogLevel::WARNING, (string) $message, $context);
}
/**
* {@inheritdoc}
*/
public function notice($message, array $context = array())
{
$this->addEntry(LogLevel::NOTICE, (string) $message, $context);
}
/**
* {@inheritdoc}
*/
public function info($message, array $context = array())
{
$this->addEntry(LogLevel::INFO, (string) $message, $context);
}
/**
* {@inheritdoc}
*/
public function debug($message, array $context = array())
{
$this->addEntry(LogLevel::DEBUG, (string) $message, $context);
}
/**
* {@inheritdoc}
*/
public function log($level, $message, array $context = array())
{
$this->addEntry($level, (string) $message, $context);
}
}

View file

@ -1,69 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util\Logger\Monolog;
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;
/**
* @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($developerIp, $level = Logger::DEBUG, $bubble = true)
{
parent::__construct($level, $bubble);
$this->developerIp = $developerIp;
}
/**
* {@inheritdoc}
*/
public function handle(array $record)
{
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) && $_SERVER['REMOTE_ADDR'] != $this->developerIp)
{
return false;
}
return false === $this->bubble;
}
}

View file

@ -1,62 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util\Logger\Monolog;
use Friendica\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(array('Monolog\\'));
$this->introspection = $introspection;
}
public function __invoke(array $record)
{
// 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;
}
}

View file

@ -1,146 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util\Logger;
use Friendica\Core\System;
use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
/**
* This Logger adds additional profiling data in case profiling is enabled.
* It uses a predefined logger.
*/
class ProfilerLogger implements LoggerInterface
{
/**
* The Logger of the current call
* @var LoggerInterface
*/
private $logger;
/**
* The Profiler for the current call
* @var Profiler
*/
protected $profiler;
/**
* ProfilerLogger constructor.
* @param LoggerInterface $logger The Logger of the current call
* @param Profiler $profiler The profiler of the current call
*/
public function __construct(LoggerInterface $logger, Profiler $profiler)
{
$this->logger = $logger;
$this->profiler = $profiler;
}
/**
* {@inheritdoc}
*/
public function emergency($message, array $context = array())
{
$this->profiler->startRecording('file');
$this->logger->emergency($message, $context);
$this->profiler->stopRecording();
}
/**
* {@inheritdoc}
*/
public function alert($message, array $context = array())
{
$this->profiler->startRecording('file');
$this->logger->alert($message, $context);
$this->profiler->stopRecording();
}
/**
* {@inheritdoc}
*/
public function critical($message, array $context = array())
{
$this->profiler->startRecording('file');
$this->logger->critical($message, $context);
$this->profiler->stopRecording();
}
/**
* {@inheritdoc}
*/
public function error($message, array $context = array())
{
$this->profiler->startRecording('file');
$this->logger->error($message, $context);
$this->profiler->stopRecording();
}
/**
* {@inheritdoc}
*/
public function warning($message, array $context = array())
{
$this->profiler->startRecording('file');
$this->logger->warning($message, $context);
$this->profiler->stopRecording();
}
/**
* {@inheritdoc}
*/
public function notice($message, array $context = array())
{
$this->profiler->startRecording('file');
$this->logger->notice($message, $context);
$this->profiler->stopRecording();
}
/**
* {@inheritdoc}
*/
public function info($message, array $context = array())
{
$this->profiler->startRecording('file');
$this->logger->info($message, $context);
$this->profiler->stopRecording();
}
/**
* {@inheritdoc}
*/
public function debug($message, array $context = array())
{
$this->profiler->startRecording('file');
$this->logger->debug($message, $context);
$this->profiler->stopRecording();
}
/**
* {@inheritdoc}
*/
public function log($level, $message, array $context = array())
{
$this->profiler->startRecording('file');
$this->logger->log($level, $message, $context);
$this->profiler->stopRecording();
}
}

View file

@ -1,27 +0,0 @@
## Friendica\Util\Logger
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
[`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.

View file

@ -1,183 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util\Logger;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\FileSystem;
use Friendica\Util\Introspection;
use Psr\Log\LogLevel;
/**
* A Logger instance for logging into a stream (file, stdout, stderr)
*/
class StreamLogger extends AbstractLogger
{
/**
* The minimum loglevel at which this logger will be triggered
* @var string
*/
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
*/
private $stream;
/**
* The current process ID
* @var int
*/
private $pid;
/**
* @var FileSystem
*/
private $fileSystem;
/**
* Translates LogLevel log levels to integer values
* @var array
*/
private $levelToInt = [
LogLevel::EMERGENCY => 0,
LogLevel::ALERT => 1,
LogLevel::CRITICAL => 2,
LogLevel::ERROR => 3,
LogLevel::WARNING => 4,
LogLevel::NOTICE => 5,
LogLevel::INFO => 6,
LogLevel::DEBUG => 7,
];
/**
* {@inheritdoc}
* @param string|resource $stream The stream to write with this logger (either a file or a stream, i.e. stdout)
* @param string $level The minimum loglevel at which this logger will be triggered
*
* @throws \Exception
*/
public function __construct($channel, $stream, Introspection $introspection, FileSystem $fileSystem, $level = LogLevel::DEBUG)
{
$this->fileSystem = $fileSystem;
parent::__construct($channel, $introspection);
if (is_resource($stream)) {
$this->stream = $stream;
} elseif (is_string($stream)) {
$this->url = $stream;
} else {
throw new \InvalidArgumentException('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 \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level));
}
$this->checkStream();
}
public function close()
{
if ($this->url && is_resource($this->stream)) {
fclose($this->stream);
}
$this->stream = null;
}
/**
* Adds a new entry to the log
*
* @param int $level
* @param string $message
* @param array $context
*
* @return void
*/
protected function addEntry($level, $message, $context = [])
{
if (!array_key_exists($level, $this->levelToInt)) {
throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level));
}
$logLevel = $this->levelToInt[$level];
if ($logLevel > $this->logLevel) {
return;
}
$this->checkStream();
$formattedLog = $this->formatLog($level, $message, $context);
fwrite($this->stream, $formattedLog);
}
/**
* Formats a log record for the syslog output
*
* @param int $level The loglevel/priority
* @param string $message The message
* @param array $context The context of this call
*
* @return string the formatted syslog output
*/
private function formatLog($level, $message, $context = [])
{
$record = $this->introspection->getRecord();
$record = array_merge($record, ['uid' => $this->logUid, 'process_id' => $this->pid]);
$logMessage = '';
$logMessage .= DateTimeFormat::utcNow(DateTimeFormat::ATOM) . ' ';
$logMessage .= $this->channel . ' ';
$logMessage .= '[' . strtoupper($level) . ']: ';
$logMessage .= $this->psrInterpolate($message, $context) . ' ';
$logMessage .= $this->jsonEncodeArray($context) . ' - ';
$logMessage .= $this->jsonEncodeArray($record);
$logMessage .= PHP_EOL;
return $logMessage;
}
private function checkStream()
{
if (is_resource($this->stream)) {
return;
}
if (empty($this->url)) {
throw new \LogicException('Missing stream URL.');
}
$this->stream = $this->fileSystem->createStream($this->url);
}
}

View file

@ -1,225 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util\Logger;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Util\Introspection;
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
{
const IDENT = 'Friendica';
/**
* Translates LogLevel log levels to syslog log priorities.
* @var array
*/
private $logLevels = [
LogLevel::DEBUG => LOG_DEBUG,
LogLevel::INFO => LOG_INFO,
LogLevel::NOTICE => LOG_NOTICE,
LogLevel::WARNING => LOG_WARNING,
LogLevel::ERROR => LOG_ERR,
LogLevel::CRITICAL => LOG_CRIT,
LogLevel::ALERT => LOG_ALERT,
LogLevel::EMERGENCY => LOG_EMERG,
];
/**
* Translates log priorities to string outputs
* @var array
*/
private $logToString = [
LOG_DEBUG => 'DEBUG',
LOG_INFO => 'INFO',
LOG_NOTICE => 'NOTICE',
LOG_WARNING => 'WARNING',
LOG_ERR => 'ERROR',
LOG_CRIT => 'CRITICAL',
LOG_ALERT => 'ALERT',
LOG_EMERG => 'EMERGENCY'
];
/**
* Indicates what logging options will be used when generating a log message
* @see http://php.net/manual/en/function.openlog.php#refsect1-function.openlog-parameters
*
* @var int
*/
private $logOpts;
/**
* Used to specify what type of program is logging the message
* @see http://php.net/manual/en/function.openlog.php#refsect1-function.openlog-parameters
*
* @var int
*/
private $logFacility;
/**
* The minimum loglevel at which this logger will be triggered
* @var int
*/
private $logLevel;
/**
* A error message of the current operation
* @var string
*/
private $errorMessage;
/**
* {@inheritdoc}
* @param string $level The minimum loglevel at which this logger will be triggered
* @param int $logOpts Indicates what logging options will be used when generating a log message
* @param int $logFacility Used to specify what type of program is logging the message
*
* @throws \Exception
*/
public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER)
{
parent::__construct($channel, $introspection);
$this->logOpts = $logOpts;
$this->logFacility = $logFacility;
$this->logLevel = $this->mapLevelToPriority($level);
$this->introspection->addClasses(array(self::class));
}
/**
* Adds a new entry to the syslog
*
* @param int $level
* @param string $message
* @param array $context
*
* @throws InternalServerErrorException if the syslog isn't available
*/
protected function addEntry($level, $message, $context = [])
{
$logLevel = $this->mapLevelToPriority($level);
if ($logLevel > $this->logLevel) {
return;
}
$formattedLog = $this->formatLog($logLevel, $message, $context);
$this->write($logLevel, $formattedLog);
}
/**
* Maps the LogLevel (@see LogLevel) to a SysLog priority (@see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters)
*
* @param string $level A LogLevel
*
* @return int The SysLog priority
*
* @throws \Psr\Log\InvalidArgumentException If the loglevel isn't valid
*/
public function mapLevelToPriority($level)
{
if (!array_key_exists($level, $this->logLevels)) {
throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level));
}
return $this->logLevels[$level];
}
/**
* Closes the Syslog
*/
public function close()
{
closelog();
}
/**
* Writes a message to the syslog
* @see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters
*
* @param int $priority The Priority
* @param string $message The message of the log
*
* @throws InternalServerErrorException if syslog cannot be used
*/
private function write($priority, $message)
{
set_error_handler([$this, 'customErrorHandler']);
$opened = openlog(self::IDENT, $this->logOpts, $this->logFacility);
restore_error_handler();
if (!$opened) {
throw new \UnexpectedValueException(sprintf('Can\'t open syslog for ident "%s" and facility "%s": ' . $this->errorMessage, $this->channel, $this->logFacility));
}
$this->syslogWrapper($priority, $message);
}
/**
* Formats a log record for the syslog output
*
* @param int $level The loglevel/priority
* @param string $message The message
* @param array $context The context of this call
*
* @return string the formatted syslog output
*/
private function formatLog($level, $message, $context = [])
{
$record = $this->introspection->getRecord();
$record = array_merge($record, ['uid' => $this->logUid]);
$logMessage = '';
$logMessage .= $this->channel . ' ';
$logMessage .= '[' . $this->logToString[$level] . ']: ';
$logMessage .= $this->psrInterpolate($message, $context) . ' ';
$logMessage .= $this->jsonEncodeArray($context) . ' - ';
$logMessage .= $this->jsonEncodeArray($record);
return $logMessage;
}
private function customErrorHandler($code, $msg)
{
$this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg);
}
/**
* A syslog wrapper to make syslog functionality testable
*
* @param int $level The syslog priority
* @param string $entry The message to send to the syslog function
*/
protected function syslogWrapper($level, $entry)
{
set_error_handler([$this, 'customErrorHandler']);
$written = syslog($level, $entry);
restore_error_handler();
if (!$written) {
throw new \UnexpectedValueException(sprintf('Can\'t write into syslog for ident "%s" and facility "%s": ' . $this->errorMessage, $this->channel, $this->logFacility));
}
}
}

View file

@ -1,159 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util\Logger;
use Psr\Log\LoggerInterface;
/**
* A Logger instance to not log
*/
class VoidLogger implements LoggerInterface
{
/**
* System is unusable.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function emergency($message, array $context = array())
{
return;
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function alert($message, array $context = array())
{
return;
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function critical($message, array $context = array())
{
return;
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function error($message, array $context = array())
{
return;
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function warning($message, array $context = array())
{
return;
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function notice($message, array $context = array())
{
return;
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function info($message, array $context = array())
{
return;
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function debug($message, array $context = array())
{
return;
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @return void
*/
public function log($level, $message, array $context = array())
{
return;
}
}

View file

@ -1,228 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Util\Logger;
use Friendica\Util\Strings;
use Psr\Log\LoggerInterface;
/**
* A Logger for specific worker tasks, which adds an additional woker-id to it.
* Uses the decorator pattern (https://en.wikipedia.org/wiki/Decorator_pattern)
*/
class WorkerLogger implements LoggerInterface
{
/**
* @var LoggerInterface The original Logger instance
*/
private $logger;
/**
* @var string the current worker ID
*/
private $workerId;
/**
* @var string The called function name
*/
private $functionName;
/**
* @param LoggerInterface $logger The logger for worker entries
* @param string $functionName The current function name of the worker
* @param int $idLength The length of the generated worker ID
*/
public function __construct(LoggerInterface $logger, $functionName = '', $idLength = 7)
{
$this->logger = $logger;
$this->functionName = $functionName;
$this->workerId = Strings::getRandomHex($idLength);
}
/**
* Sets the function name for additional logging
*
* @param string $functionName
*/
public function setFunctionName(string $functionName)
{
$this->functionName = $functionName;
}
/**
* Adds the worker context for each log entry
*
* @param array $context
*/
private function addContext(array &$context)
{
$context['worker_id'] = $this->workerId;
$context['worker_cmd'] = $this->functionName;
}
/**
* Returns the worker ID
*
* @return string
*/
public function getWorkerId()
{
return $this->workerId;
}
/**
* System is unusable.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function emergency($message, array $context = [])
{
$this->addContext($context);
$this->logger->emergency($message, $context);
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function alert($message, array $context = [])
{
$this->addContext($context);
$this->logger->alert($message, $context);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function critical($message, array $context = [])
{
$this->addContext($context);
$this->logger->critical($message, $context);
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function error($message, array $context = [])
{
$this->addContext($context);
$this->logger->error($message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function warning($message, array $context = [])
{
$this->addContext($context);
$this->logger->warning($message, $context);
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function notice($message, array $context = [])
{
$this->addContext($context);
$this->logger->notice($message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function info($message, array $context = [])
{
$this->addContext($context);
$this->logger->info($message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function debug($message, array $context = [])
{
$this->addContext($context);
$this->logger->debug($message, $context);
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @return void
*/
public function log($level, $message, array $context = [])
{
$this->addContext($context);
$this->logger->log($level, $message, $context);
}
}

View file

@ -30,7 +30,7 @@ use Friendica\Database\Database;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Network\HTTPException;
use Friendica\Network\HTTPClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
/**
* Get information about a given URL
@ -214,7 +214,7 @@ class ParseUrl
return $siteinfo;
}
$curlResult = DI::httpClient()->get($url, [HTTPClientOptions::CONTENT_LENGTH => 1000000]);
$curlResult = DI::httpClient()->get($url, [HttpClientOptions::CONTENT_LENGTH => 1000000]);
if (!$curlResult->isSuccess() || empty($curlResult->getBody())) {
return $siteinfo;
}

View file

@ -21,8 +21,8 @@
namespace Friendica\Util;
use Friendica\Core\Config\Cache;
use Friendica\Core\Config\IConfig;
use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\System;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
@ -69,16 +69,16 @@ class Profiler implements ContainerInterface
/**
* Updates the enabling of the current profiler
*
* @param IConfig $config
* @param IManageConfigValues $config
*/
public function update(IConfig $config)
public function update(IManageConfigValues $config)
{
$this->enabled = $config->get('system', 'profiler');
$this->rendertime = $config->get('rendertime', 'callstack');
}
/**
* @param Cache $configCache The configuration cache
* @param \Friendica\Core\Config\ValueObject\Cache $configCache The configuration cache
*/
public function __construct(Cache $configCache)
{