$configLoader = new Config\ConfigCacheLoader($basedir);
$config = Factory\ConfigFactory::createCache($configLoader);
$logger = Factory\LoggerFactory::create('auth_ejabberd', $config);
+$profiler = Factory\ProfilerFactory::create($config);
-$a = new App($config, $logger);
+$a = new App($config, $logger, $profiler);
if ($a->getMode()->isNormal()) {
$oAuth = new ExAuth();
$configLoader = new Config\ConfigCacheLoader($basedir);
$config = Factory\ConfigFactory::createCache($configLoader);
$logger = Factory\LoggerFactory::create('console', $config);
+$profiler = Factory\ProfilerFactory::create($config);
-$a = new Friendica\App($config, $logger);
+$a = new Friendica\App($config, $logger, $profiler);
\Friendica\BaseObject::setApp($a);
(new Friendica\Core\Console($argv))->execute();
$configLoader = new Config\ConfigCacheLoader($basedir);
$config = Factory\ConfigFactory::createCache($configLoader);
$logger = Factory\LoggerFactory::create('daemon', $config);
+$profiler = Factory\ProfilerFactory::create($config);
-$a = new App($config, $logger);
+$a = new App($config, $logger, $profiler);
if ($a->getMode()->isInstall()) {
die("Friendica isn't properly installed yet.\n");
$configLoader = new Config\ConfigCacheLoader($basedir);
$config = Factory\ConfigFactory::createCache($configLoader);
$logger = Factory\LoggerFactory::create('worker', $config);
+$profiler = Factory\ProfilerFactory::create($config);
-$a = new App($config, $logger);
+$a = new App($config, $logger, $profiler);
// Check the database structure and possibly fixes it
Update::check($a->getBasePath(), true);
Logger::info(API_LOG_PREFIX . 'username {username}', ['module' => 'api', 'action' => 'call', 'username' => $a->user['username'], 'duration' => round($duration, 2)]);
- if (Config::get("system", "profiler")) {
- $duration = microtime(true)-$a->performance["start"];
-
- /// @TODO round() really everywhere?
- Logger::debug(
- API_LOG_PREFIX . 'performance',
- [
- 'module' => 'api',
- 'action' => 'call',
- 'database_read' => round($a->performance["database"] - $a->performance["database_write"], 3),
- 'database_write' => round($a->performance["database_write"], 3),
- 'cache_read' => round($a->performance["cache"], 3),
- 'cache_write' => round($a->performance["cache_write"], 3),
- 'network_io' => round($a->performance["network"], 2),
- 'file_io' => round($a->performance["file"], 2),
- 'other_io' => round($duration - ($a->performance["database"]
- + $a->performance["cache"] + $a->performance["cache_write"]
- + $a->performance["network"] + $a->performance["file"]), 2),
- 'total' => round($duration, 2)
- ]
- );
-
- if (Config::get("rendertime", "callstack")) {
- $o = "Database Read:\n";
- foreach ($a->callstack["database"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func . ": " . $time . "\n";
- }
- }
- $o .= "\nDatabase Write:\n";
- foreach ($a->callstack["database_write"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func . ": " . $time . "\n";
- }
- }
-
- $o = "Cache Read:\n";
- foreach ($a->callstack["cache"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func . ": " . $time . "\n";
- }
- }
- $o .= "\nCache Write:\n";
- foreach ($a->callstack["cache_write"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func . ": " . $time . "\n";
- }
- }
-
- $o .= "\nNetwork:\n";
- foreach ($a->callstack["network"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func . ": " . $time . "\n";
- }
- }
- Logger::debug(API_LOG_PREFIX . $o, ['module' => 'api', 'action' => 'call']);
- }
- }
+ $a->getProfiler()->saveLog(API_LOG_PREFIX . 'performance');
if (false === $return) {
/*
$configLoader = new Config\ConfigCacheLoader($basedir);
$config = Factory\ConfigFactory::createCache($configLoader);
$logger = Factory\LoggerFactory::create('index', $config);
+$profiler = Factory\ProfilerFactory::create($config);
// We assume that the index.php is called by a frontend process
// The value is set to "true" by default in App
-$a = new App($config, $logger, false);
+$a = new App($config, $logger, $profiler, false);
$a->runFrontend();
use Friendica\Database\DBA;
use Friendica\Factory\ConfigFactory;
use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
/**
public $identities;
public $is_mobile = false;
public $is_tablet = false;
- public $performance = [];
- public $callstack = [];
public $theme_info = [];
public $category;
// Allow themes to control internal parameters
*/
private $config;
+ /**
+ * @var Profiler The profiler of this app
+ */
+ private $profiler;
+
/**
* Returns the current config cache of this node
*
return $this->basePath;
}
+ /**
+ * The profiler of this app
+ *
+ * @return Profiler
+ */
+ public function getProfiler()
+ {
+ return $this->profiler;
+ }
+
/**
* Register a stylesheet file path to be included in the <head> tag of every page.
* Inclusion is done in App->initHead().
*
* @param ConfigCache $config The Cached Config
* @param LoggerInterface $logger Logger of this application
+ * @param Profiler $profiler The profiler of this application
* @param bool $isBackend Whether it is used for backend or frontend (Default true=backend)
*
* @throws Exception if the Basepath is not usable
*/
- public function __construct(ConfigCache $config, LoggerInterface $logger, $isBackend = true)
+ public function __construct(ConfigCache $config, LoggerInterface $logger, Profiler $profiler, $isBackend = true)
{
$this->config = $config;
$this->logger = $logger;
+ $this->profiler = $profiler;
$this->basePath = $this->config->get('system', 'basepath');
if (!Core\System::isDirectoryUsable($this->basePath, false)) {
$this->checkBackend($isBackend);
$this->checkFriendicaApp();
- $this->performance['start'] = microtime(true);
- $this->performance['database'] = 0;
- $this->performance['database_write'] = 0;
- $this->performance['cache'] = 0;
- $this->performance['cache_write'] = 0;
- $this->performance['network'] = 0;
- $this->performance['file'] = 0;
- $this->performance['rendering'] = 0;
- $this->performance['parser'] = 0;
- $this->performance['marktime'] = 0;
- $this->performance['markstart'] = microtime(true);
-
- $this->callstack['database'] = [];
- $this->callstack['database_write'] = [];
- $this->callstack['cache'] = [];
- $this->callstack['cache_write'] = [];
- $this->callstack['network'] = [];
- $this->callstack['file'] = [];
- $this->callstack['rendering'] = [];
- $this->callstack['parser'] = [];
+ $this->profiler->reset();
$this->mode = new App\Mode($this->basePath);
$stamp1 = microtime(true);
- if (DBA::connect($this->config, $db_host, $db_user, $db_pass, $db_data, $charset)) {
+ if (DBA::connect($this->config, $this->profiler, $db_host, $db_user, $db_pass, $db_data, $charset)) {
// Loads DB_UPDATE_VERSION constant
Database\DBStructure::definition($this->basePath, false);
}
unset($db_host, $db_user, $db_pass, $db_data, $charset);
- $this->saveTimestamp($stamp1, 'network');
+ $this->profiler->saveTimestamp($stamp1, 'network');
}
public function getScheme()
}
}
- /**
- * Saves a timestamp for a value - f.e. a call
- * Necessary for profiling Friendica
- *
- * @param int $timestamp the Timestamp
- * @param string $value A value to profile
- */
- public function saveTimestamp($timestamp, $value)
- {
- $profiler = $this->config->get('system', 'profiler');
-
- if (!isset($profiler) || !$profiler) {
- return;
- }
-
- $duration = (float) (microtime(true) - $timestamp);
-
- if (!isset($this->performance[$value])) {
- // Prevent ugly E_NOTICE
- $this->performance[$value] = 0;
- }
-
- $this->performance[$value] += (float) $duration;
- $this->performance['marktime'] += (float) $duration;
-
- $callstack = Core\System::callstack();
-
- if (!isset($this->callstack[$value][$callstack])) {
- // Prevent ugly E_NOTICE
- $this->callstack[$value][$callstack] = 0;
- }
-
- $this->callstack[$value][$callstack] += (float) $duration;
- }
-
/**
* Returns the current UserAgent as a String
*
if (!$this->isBackend()) {
$stamp1 = microtime(true);
session_start();
- $this->saveTimestamp($stamp1, 'parser');
+ $this->profiler->saveTimestamp($stamp1, 'parser');
Core\L10n::setSessionVariable();
Core\L10n::setLangFromSession();
} else {
@curl_exec($ch);
$curl_info = @curl_getinfo($ch);
- $a->saveTimestamp($stamp1, "network");
+ $a->getProfiler()->saveTimestamp($stamp1, "network");
if (substr($curl_info["content_type"], 0, 6) == "image/") {
$text = "[url=" . $match[1] . "]" . $match[1] . "[/url]";
@curl_exec($ch);
$curl_info = @curl_getinfo($ch);
- $a->saveTimestamp($stamp1, "network");
+ $a->getProfiler()->saveTimestamp($stamp1, "network");
// if its a link to a picture then embed this picture
if (substr($curl_info["content_type"], 0, 6) == "image/") {
// unmask the special chars back to HTML
$text = str_replace(['&\_lt\_;', '&\_gt\_;', '&\_amp\_;'], ['<', '>', '&'], $text);
- $a->saveTimestamp($stamp1, "parser");
+ $a->getProfiler()->saveTimestamp($stamp1, "parser");
// Libertree has a problem with escaped hashtags.
$text = str_replace(['\#'], ['#'], $text);
$html = $MarkdownParser->transform($text);
$html = preg_replace('/<a(.*?)href="#/is', '<a$1href="' . ltrim($_SERVER['REQUEST_URI'], '/') . '#', $html);
- self::getApp()->saveTimestamp($stamp1, "parser");
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, "parser");
return $html;
}
$stamp1 = microtime(true);
$f = file_get_contents("addon/$addon/$addon.php");
- $a->saveTimestamp($stamp1, "file");
+ $a->getProfiler()->saveTimestamp($stamp1, "file");
$r = preg_match("|/\*.*\*/|msU", $f, $m);
$return = self::getDriver()->getAllKeys($prefix);
- self::getApp()->saveTimestamp($time, 'cache');
+ self::getApp()->getProfiler()->saveTimestamp($time, 'cache');
return $return;
}
$return = self::getDriver()->get($key);
- self::getApp()->saveTimestamp($time, 'cache');
+ self::getApp()->getProfiler()->saveTimestamp($time, 'cache');
return $return;
}
$return = self::getDriver()->set($key, $value, $duration);
- self::getApp()->saveTimestamp($time, 'cache_write');
+ self::getApp()->getProfiler()->saveTimestamp($time, 'cache_write');
return $return;
}
$return = self::getDriver()->delete($key);
- self::getApp()->saveTimestamp($time, 'cache_write');
+ self::getApp()->getProfiler()->saveTimestamp($time, 'cache_write');
return $return;
}
$installer->resetChecks();
- if (!$installer->checkDB($a->getConfig(), $db_host, $db_user, $db_pass, $db_data)) {
+ if (!$installer->checkDB($a->getConfig(), $a->getProfiler(), $db_host, $db_user, $db_pass, $db_data)) {
$errorMessage = $this->extractErrors($installer->getChecks());
throw new RuntimeException($errorMessage);
}
use Friendica\Database\DBStructure;
use Friendica\Object\Image;
use Friendica\Util\Network;
+use Friendica\Util\Profiler;
use Friendica\Util\Strings;
/**
* Checking the Database connection and if it is available for the current installation
*
* @param ConfigCache $configCache The configuration cache
+ * @param Profiler $profiler The profiler of this app
* @param string $dbhost Hostname/IP of the Friendica Database
* @param string $dbuser Username of the Database connection credentials
* @param string $dbpass Password of the Database connection credentials
* @return bool true if the check was successful, otherwise false
* @throws Exception
*/
- public function checkDB(ConfigCache $configCache, $dbhost, $dbuser, $dbpass, $dbdata)
+ public function checkDB(ConfigCache $configCache, Profiler $profiler, $dbhost, $dbuser, $dbpass, $dbdata)
{
- if (!DBA::connect($configCache, $dbhost, $dbuser, $dbpass, $dbdata)) {
+ if (!DBA::connect($configCache, $profiler, $dbhost, $dbuser, $dbpass, $dbdata)) {
$this->addCheck(L10n::t('Could not connect to database.'), false, true, '');
return false;
$stamp1 = microtime(true);
self::$logger->emergency($message, $context);
- self::getApp()->saveTimestamp($stamp1, 'file');
+ self::getApp()->GetProfiler()->saveTimestamp($stamp1, 'file');
}
/**
$stamp1 = microtime(true);
self::$logger->alert($message, $context);
- self::getApp()->saveTimestamp($stamp1, 'file');
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file');
}
/**
$stamp1 = microtime(true);
self::$logger->critical($message, $context);
- self::getApp()->saveTimestamp($stamp1, 'file');
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file');
}
/**
$stamp1 = microtime(true);
self::$logger->error($message, $context);
- self::getApp()->saveTimestamp($stamp1, 'file');
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file');
}
/**
$stamp1 = microtime(true);
self::$logger->warning($message, $context);
- self::getApp()->saveTimestamp($stamp1, 'file');
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file');
}
/**
$stamp1 = microtime(true);
self::$logger->notice($message, $context);
- self::getApp()->saveTimestamp($stamp1, 'file');
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file');
}
/**
$stamp1 = microtime(true);
self::$logger->info($message, $context);
- self::getApp()->saveTimestamp($stamp1, 'file');
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file');
}
/**
$stamp1 = microtime(true);
self::$logger->debug($message, $context);
- self::getApp()->saveTimestamp($stamp1, 'file');
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file');
}
/**
$stamp1 = microtime(true);
self::$logger->log($level, $msg);
- self::getApp()->saveTimestamp($stamp1, "file");
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, "file");
}
/**
$stamp1 = microtime(true);
self::$devLogger->log($level, $msg);
- self::getApp()->saveTimestamp($stamp1, "file");
+ self::getApp()->getProfiler()->saveTimestamp($stamp1, "file");
}
}
exit();
}
- $a->saveTimestamp($stamp1, "rendering");
+ $a->getProfiler()->saveTimestamp($stamp1, "rendering");
return $output;
}
exit();
}
- $a->saveTimestamp($stamp1, "file");
+ $a->getProfiler()->saveTimestamp($stamp1, "file");
return $template;
}
$a = \get_app();
$stamp1 = microtime(true);
$theme_file = file_get_contents("view/theme/$theme/theme.php");
- $a->saveTimestamp($stamp1, "file");
+ $a->getProfiler()->saveTimestamp($stamp1, "file");
$result = preg_match("|/\*.*\*/|msU", $theme_file, $matches);
// We use the callstack here to analyze the performance of executed worker entries.
// For this reason the variables have to be initialized.
- if (Config::get("system", "profiler")) {
- $a->performance["start"] = microtime(true);
- $a->performance["database"] = 0;
- $a->performance["database_write"] = 0;
- $a->performance["cache"] = 0;
- $a->performance["cache_write"] = 0;
- $a->performance["network"] = 0;
- $a->performance["file"] = 0;
- $a->performance["rendering"] = 0;
- $a->performance["parser"] = 0;
- $a->performance["marktime"] = 0;
- $a->performance["markstart"] = microtime(true);
- $a->callstack = [];
- }
+ $a->getProfiler()->reset();
// For better logging create a new process id for every worker call
// But preserve the old one for the worker
Logger::log("Process ".$mypid." - Prio ".$queue["priority"]." - ID ".$queue["id"].": ".$funcname." - done in ".number_format($duration, 4)." seconds. Process PID: ".$new_process_id);
- // Write down the performance values into the log
- if (Config::get("system", "profiler")) {
- $duration = microtime(true)-$a->performance["start"];
-
- $o = '';
- if (Config::get("rendertime", "callstack")) {
- if (isset($a->callstack["database"])) {
- $o .= "\nDatabase Read:\n";
- foreach ($a->callstack["database"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func.": ".$time."\n";
- }
- }
- }
- if (isset($a->callstack["database_write"])) {
- $o .= "\nDatabase Write:\n";
- foreach ($a->callstack["database_write"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func.": ".$time."\n";
- }
- }
- }
- if (isset($a->callstack["dache"])) {
- $o .= "\nCache Read:\n";
- foreach ($a->callstack["dache"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func.": ".$time."\n";
- }
- }
- }
- if (isset($a->callstack["dache_write"])) {
- $o .= "\nCache Write:\n";
- foreach ($a->callstack["dache_write"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func.": ".$time."\n";
- }
- }
- }
- if (isset($a->callstack["network"])) {
- $o .= "\nNetwork:\n";
- foreach ($a->callstack["network"] as $func => $time) {
- $time = round($time, 3);
- if ($time > 0) {
- $o .= $func.": ".$time."\n";
- }
- }
- }
- }
-
- Logger::log(
- "ID ".$queue["id"].": ".$funcname.": ".sprintf(
- "DB: %s/%s, Cache: %s/%s, Net: %s, I/O: %s, Other: %s, Total: %s".$o,
- number_format($a->performance["database"] - $a->performance["database_write"], 2),
- number_format($a->performance["database_write"], 2),
- number_format($a->performance["cache"], 2),
- number_format($a->performance["cache_write"], 2),
- number_format($a->performance["network"], 2),
- number_format($a->performance["file"], 2),
- number_format($duration - ($a->performance["database"]
- + $a->performance["cache"] + $a->performance["cache_write"]
- + $a->performance["network"] + $a->performance["file"]), 2),
- number_format($duration, 2)
- ),
- Logger::DEBUG
- );
- }
+ $a->getProfiler()->saveLog("ID " . $queue["id"] . ": " . $funcname);
$cooldown = Config::get("system", "worker_cooldown", 0);
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Profiler;
use mysqli;
use mysqli_result;
use mysqli_stmt;
* @var IConfigCache
*/
private static $configCache;
+ /**
+ * @var Profiler
+ */
+ private static $profiler;
private static $server_info = '';
private static $connection;
private static $driver;
private static $db_name = '';
private static $db_charset = '';
- public static function connect($configCache, $serveraddr, $user, $pass, $db, $charset = null)
+ public static function connect(IConfigCache $configCache, Profiler $profiler, $serveraddr, $user, $pass, $db, $charset = null)
{
if (!is_null(self::$connection) && self::connected()) {
return true;
// We are storing these values for being able to perform a reconnect
self::$configCache = $configCache;
+ self::$profiler = $profiler;
self::$db_serveraddr = $serveraddr;
self::$db_user = $user;
self::$db_pass = $pass;
public static function reconnect() {
self::disconnect();
- $ret = self::connect(self::$configCache, self::$db_serveraddr, self::$db_user, self::$db_pass, self::$db_name, self::$db_charset);
+ $ret = self::connect(self::$configCache, self::$profiler, self::$db_serveraddr, self::$db_user, self::$db_pass, self::$db_name, self::$db_charset);
return $ret;
}
* @throws \Exception
*/
public static function p($sql) {
- $a = \get_app();
$stamp1 = microtime(true);
self::$errorno = $errorno;
}
- $a->saveTimestamp($stamp1, 'database');
+ self::$profiler->saveTimestamp($stamp1, 'database');
if (self::$configCache->get('system', 'db_log')) {
$stamp2 = microtime(true);
* @throws \Exception
*/
public static function e($sql) {
- $a = \get_app();
$stamp = microtime(true);
self::$errorno = $errorno;
}
- $a->saveTimestamp($stamp, "database_write");
+ self::$profiler->saveTimestamp($stamp, "database_write");
return $retval;
}
* @return array current row
*/
public static function fetch($stmt) {
- $a = \get_app();
$stamp1 = microtime(true);
}
}
- $a->saveTimestamp($stamp1, 'database');
+ self::$profiler->saveTimestamp($stamp1, 'database');
return $columns;
}
* @return boolean was the close successful?
*/
public static function close($stmt) {
- $a = \get_app();
$stamp1 = microtime(true);
break;
}
- $a->saveTimestamp($stamp1, 'database');
+ self::$profiler->saveTimestamp($stamp1, 'database');
return $ret;
}
--- /dev/null
+<?php
+
+namespace Friendica\Factory;
+
+use Friendica\Core\Config\ConfigCache;
+use Friendica\Util\Profiler;
+use Psr\Log\LoggerInterface;
+
+class ProfilerFactory
+{
+ /**
+ * Creates a Profiler for the current execution
+ *
+ * @param LoggerInterface $logger The logger for saving the profiling data
+ * @param ConfigCache $configCache The configuration cache
+ *
+ * @return Profiler
+ */
+ public static function create(LoggerInterface $logger, ConfigCache $configCache)
+ {
+ $enabled = $configCache->get('system', 'profiler', false);
+ $renderTime = $configCache->get('rendertime', 'callstack', false);
+ return new Profiler($logger, $enabled, $renderTime);
+ }
+}
$dbdata = Strings::escapeTags(trim(defaults($_POST, 'dbdata', '')));
// If we cannot connect to the database, return to the previous step
- if (!self::$installer->checkDB($a->getConfig(), $dbhost, $dbuser, $dbpass, $dbdata)) {
+ if (!self::$installer->checkDB($a->getConfig(), $a->getProfiler(), $dbhost, $dbuser, $dbpass, $dbdata)) {
self::$currentWizardStep = self::DATABASE_CONFIG;
}
$adminmail = Strings::escapeTags(trim(defaults($_POST, 'adminmail', '')));
// If we cannot connect to the database, return to the Database config wizard
- if (!self::$installer->checkDB($a->getConfig(), $dbhost, $dbuser, $dbpass, $dbdata)) {
+ if (!self::$installer->checkDB($a->getConfig(), $a->getProfiler(), $dbhost, $dbuser, $dbpass, $dbdata)) {
self::$currentWizardStep = self::DATABASE_CONFIG;
return;
}
*/
namespace Friendica\Object;
+use Exception;
use Friendica\App;
use Friendica\Core\Cache;
use Friendica\Core\Config;
use Friendica\Database\DBA;
use Friendica\Model\Photo;
use Friendica\Util\Network;
-use Exception;
use Imagick;
use ImagickPixel;
$stamp1 = microtime(true);
file_put_contents($path, $string);
- $a->saveTimestamp($stamp1, "file");
+ $a->getProfiler()->saveTimestamp($stamp1, "file");
}
/**
$a = \get_app();
$stamp1 = microtime(true);
file_put_contents($tempfile, $img_str);
- $a->saveTimestamp($stamp1, "file");
+ $a->getProfiler()->saveTimestamp($stamp1, "file");
$data = getimagesize($tempfile);
unlink($tempfile);
$stamp1 = microtime(true);
$imagedata = @file_get_contents($url);
- $a->saveTimestamp($stamp1, "file");
+ $a->getProfiler()->saveTimestamp($stamp1, "file");
}
$maximagesize = Config::get('system', 'maximagesize');
$stamp1 = microtime(true);
file_put_contents($tempfile, $imagedata);
- $a->saveTimestamp($stamp1, "file");
+ $a->getProfiler()->saveTimestamp($stamp1, "file");
$data = getimagesize($tempfile);
*/
namespace Friendica\Util;
+use DOMDocument;
+use DomXPath;
+use Friendica\Core\Config;
use Friendica\Core\Hook;
use Friendica\Core\Logger;
use Friendica\Core\System;
-use Friendica\Core\Config;
use Friendica\Network\CurlResult;
-use DOMDocument;
-use DomXPath;
class Network
{
@curl_close($ch);
- $a->saveTimestamp($stamp1, 'network');
+ $a->getProfiler()->saveTimestamp($stamp1, 'network');
return $curlResponse;
}
curl_close($ch);
- $a->saveTimestamp($stamp1, 'network');
+ $a->getProfiler()->saveTimestamp($stamp1, 'network');
Logger::log('post_url: end ' . $url, Logger::DATA);
$http_code = $curl_info['http_code'];
curl_close($ch);
- $a->saveTimestamp($stamp1, "network");
+ $a->getProfiler()->saveTimestamp($stamp1, "network");
if ($http_code == 0) {
return $url;
$body = curl_exec($ch);
curl_close($ch);
- $a->saveTimestamp($stamp1, "network");
+ $a->getProfiler()->saveTimestamp($stamp1, "network");
if (trim($body) == "") {
return $url;
--- /dev/null
+<?php
+
+namespace Friendica\Util;
+
+use Friendica\Core;
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
+use Psr\Log\LoggerInterface;
+
+/**
+ * A class to store profiling data
+ * It can handle different logging data for specific functions or global performance measures
+ *
+ * It stores the data as log entries (@see LoggerInterface )
+ */
+class Profiler implements ContainerInterface
+{
+ /**
+ * @var array The global performance array
+ */
+ private $performance;
+ /**
+ * @var array The function specific callstack
+ */
+ private $callstack;
+ /**
+ * @var bool True, if the Profiler is enabled
+ */
+ private $enabled;
+ /**
+ * @var bool True, if the Profiler should measure the whole rendertime including functions
+ */
+ private $rendertime;
+
+ /**
+ * @var LoggerInterface The profiler logger
+ */
+ private $logger;
+
+ /**
+ * @param LoggerInterface $logger The profiler logger
+ * @param bool $enabled True, if the Profiler is enabled
+ * @param bool $renderTime True, if the Profiler should measure the whole rendertime including functions
+ */
+ public function __construct(LoggerInterface $logger, $enabled = false, $renderTime = false)
+ {
+ $this->enabled = $enabled;
+ $this->rendertime = $renderTime;
+ $this->logger = $logger;
+ $this->performance = [];
+ $this->callstack = [];
+ }
+
+ /**
+ * Saves a timestamp for a value - f.e. a call
+ * Necessary for profiling Friendica
+ *
+ * @param int $timestamp the Timestamp
+ * @param string $value A value to profile
+ */
+ public function saveTimestamp($timestamp, $value)
+ {
+ if (!$this->enabled) {
+ return;
+ }
+
+ $duration = (float) (microtime(true) - $timestamp);
+
+ if (!isset($this->performance[$value])) {
+ // Prevent ugly E_NOTICE
+ $this->performance[$value] = 0;
+ }
+
+ $this->performance[$value] += (float) $duration;
+ $this->performance['marktime'] += (float) $duration;
+
+ $callstack = Core\System::callstack();
+
+ if (!isset($this->callstack[$value][$callstack])) {
+ // Prevent ugly E_NOTICE
+ $this->callstack[$value][$callstack] = 0;
+ }
+
+ $this->callstack[$value][$callstack] += (float) $duration;
+ }
+
+ /**
+ * Resets the performance and callstack profiling
+ *
+ * @param bool $performance If true, reset the performance (Default true)
+ * @param bool $callstack If true, reset the callstack (Default true)
+ */
+ public function reset($performance = true, $callstack = true)
+ {
+ if ($performance) {
+ $this->performance = [];
+ $this->performance['start'] = microtime(true);
+ $this->performance['database'] = 0;
+ $this->performance['database_write'] = 0;
+ $this->performance['cache'] = 0;
+ $this->performance['cache_write'] = 0;
+ $this->performance['network'] = 0;
+ $this->performance['file'] = 0;
+ $this->performance['rendering'] = 0;
+ $this->performance['parser'] = 0;
+ $this->performance['marktime'] = 0;
+ $this->performance['markstart'] = microtime(true);
+ }
+
+ if ($callstack) {
+ $this->callstack['database'] = [];
+ $this->callstack['database_write'] = [];
+ $this->callstack['cache'] = [];
+ $this->callstack['cache_write'] = [];
+ $this->callstack['network'] = [];
+ $this->callstack['file'] = [];
+ $this->callstack['rendering'] = [];
+ $this->callstack['parser'] = [];
+ }
+ }
+
+ /**
+ * Save the current profiling data to a log entry
+ *
+ * @param string $message Additional message for the log
+ */
+ public function saveLog($message)
+ {
+ // Write down the performance values into the log
+ if ($this->enabled) {
+ $duration = microtime(true)-$this->get('start');
+ $this->logger->info(
+ $message,
+ [
+ 'module' => 'api',
+ 'action' => 'call',
+ 'database_read' => round($this->get('database') - $this->get('database_write'), 3),
+ 'database_write' => round($this->get('database_write'), 3),
+ 'cache_read' => round($this->get('cache'), 3),
+ 'cache_write' => round($this->get('cache_write'), 3),
+ 'network_io' => round($this->get('network'), 2),
+ 'file_io' => round($this->get('file'), 2),
+ 'other_io' => round($duration - ($this->get('database')
+ + $this->get('cache') + $this->get('cache_write')
+ + $this->get('network') + $this->get('file')), 2),
+ 'total' => round($duration, 2)
+ ]
+ );
+
+ $o = '';
+ if ($this->rendertime) {
+ if (isset($this->callstack["database"])) {
+ $o .= "\nDatabase Read:\n";
+ foreach ($this->callstack["database"] as $func => $time) {
+ $time = round($time, 3);
+ if ($time > 0) {
+ $o .= $func.": ".$time."\n";
+ }
+ }
+ }
+ if (isset($this->callstack["database_write"])) {
+ $o .= "\nDatabase Write:\n";
+ foreach ($this->callstack["database_write"] as $func => $time) {
+ $time = round($time, 3);
+ if ($time > 0) {
+ $o .= $func.": ".$time."\n";
+ }
+ }
+ }
+ if (isset($this->callstack["dache"])) {
+ $o .= "\nCache Read:\n";
+ foreach ($this->callstack["dache"] as $func => $time) {
+ $time = round($time, 3);
+ if ($time > 0) {
+ $o .= $func.": ".$time."\n";
+ }
+ }
+ }
+ if (isset($this->callstack["dache_write"])) {
+ $o .= "\nCache Write:\n";
+ foreach ($this->callstack["dache_write"] as $func => $time) {
+ $time = round($time, 3);
+ if ($time > 0) {
+ $o .= $func.": ".$time."\n";
+ }
+ }
+ }
+ if (isset($this->callstack["network"])) {
+ $o .= "\nNetwork:\n";
+ foreach ($this->callstack["network"] as $func => $time) {
+ $time = round($time, 3);
+ if ($time > 0) {
+ $o .= $func.": ".$time."\n";
+ }
+ }
+ }
+ }
+
+ $this->logger->info(
+ $message . ": " . sprintf(
+ "DB: %s/%s, Cache: %s/%s, Net: %s, I/O: %s, Other: %s, Total: %s".$o,
+ number_format($this->get('database') - $this->get('database_write'), 2),
+ number_format($this->get('database_write'), 2),
+ number_format($this->get('cache'), 2),
+ number_format($this->get('cache_write'), 2),
+ number_format($this->get('network'), 2),
+ number_format($this->get('file'), 2),
+ number_format($duration - ($this->get('database')
+ + $this->get('cache') + $this->get('cache_write')
+ + $this->get('network') + $this->get('file')), 2),
+ number_format($duration, 2)
+ )
+ );
+ }
+ }
+
+ /**
+ * Finds an entry of the container by its identifier and returns it.
+ *
+ * @param string $id Identifier of the entry to look for.
+ *
+ * @throws NotFoundExceptionInterface No entry was found for **this** identifier.
+ * @throws ContainerExceptionInterface Error while retrieving the entry.
+ *
+ * @return int Entry.
+ */
+ public function get($id)
+ {
+ if (!$this->has($id)) {
+ return 0;
+ } else {
+ return $this->performance[$id];
+ }
+ }
+
+ /**
+ * Returns true if the container can return an entry for the given identifier.
+ * Returns false otherwise.
+ *
+ * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
+ * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
+ *
+ * @param string $id Identifier of the entry to look for.
+ *
+ * @return bool
+ */
+ public function has($id)
+ {
+ return isset($this->performance[$id]);
+ }
+}
use Friendica\Database\DBA;
use Friendica\Factory;
use Friendica\Util\BasePath;
+use Friendica\Util\Profiler;
use PHPUnit\DbUnit\DataSet\YamlDataSet;
use PHPUnit\DbUnit\TestCaseTrait;
use PHPUnit_Extensions_Database_DB_IDatabaseConnection;
$configLoader = new Config\ConfigCacheLoader($basedir);
$config = Factory\ConfigFactory::createCache($configLoader);
+ $profiler = \Mockery::mock(Profiler::class);
+
DBA::connect(
$config,
+ $profiler,
getenv('MYSQL_HOST'),
getenv('MYSQL_USERNAME'),
getenv('MYSQL_PASSWORD'),