Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
6.9 KiB

3 years ago
3 years ago
3 years ago
3 years ago
  1. <?php
  2. namespace Friendica\Util;
  3. use Friendica\Core\Config\Cache\ConfigCache;
  4. use Friendica\Core\Config\IConfiguration;
  5. use Psr\Container\ContainerExceptionInterface;
  6. use Psr\Container\ContainerInterface;
  7. use Psr\Container\NotFoundExceptionInterface;
  8. use Psr\Log\LoggerInterface;
  9. /**
  10. * A class to store profiling data
  11. * It can handle different logging data for specific functions or global performance measures
  12. *
  13. * It stores the data as log entries (@see LoggerInterface)
  14. */
  15. class Profiler implements ContainerInterface
  16. {
  17. /**
  18. * @var array The global performance array
  19. */
  20. private $performance;
  21. /**
  22. * @var array The function specific callstack
  23. */
  24. private $callstack;
  25. /**
  26. * @var bool True, if the Profiler is enabled
  27. */
  28. private $enabled;
  29. /**
  30. * @var bool True, if the Profiler should measure the whole rendertime including functions
  31. */
  32. private $rendertime;
  33. /**
  34. * True, if the Profiler should measure the whole rendertime including functions
  35. *
  36. * @return bool
  37. */
  38. public function isRendertime()
  39. {
  40. return $this->rendertime;
  41. }
  42. /**
  43. * Updates the enabling of the current profiler
  44. *
  45. * @param IConfiguration $config
  46. */
  47. public function update(IConfiguration $config)
  48. {
  49. $this->enabled = $config->get('system', 'profiler');
  50. $this->rendertime = $config->get('rendertime', 'callstack');
  51. }
  52. /**
  53. * @param ConfigCache $configCache The configuration cache
  54. */
  55. public function __construct(ConfigCache $configCache)
  56. {
  57. $this->enabled = $configCache->get('system', 'profiler');
  58. $this->rendertime = $configCache->get('rendertime', 'callstack');
  59. $this->reset();
  60. }
  61. /**
  62. * Saves a timestamp for a value - f.e. a call
  63. * Necessary for profiling Friendica
  64. *
  65. * @param int $timestamp the Timestamp
  66. * @param string $value A value to profile
  67. * @param string $callstack The callstack of the current profiling data
  68. */
  69. public function saveTimestamp($timestamp, $value, $callstack = '')
  70. {
  71. if (!$this->enabled) {
  72. return;
  73. }
  74. $duration = floatval(microtime(true) - $timestamp);
  75. if (!isset($this->performance[$value])) {
  76. // Prevent ugly E_NOTICE
  77. $this->performance[$value] = 0;
  78. }
  79. $this->performance[$value] += (float) $duration;
  80. $this->performance['marktime'] += (float) $duration;
  81. if (!isset($this->callstack[$value][$callstack])) {
  82. // Prevent ugly E_NOTICE
  83. $this->callstack[$value][$callstack] = 0;
  84. }
  85. $this->callstack[$value][$callstack] += (float) $duration;
  86. }
  87. /**
  88. * Resets the performance and callstack profiling
  89. */
  90. public function reset()
  91. {
  92. $this->resetPerformance();
  93. $this->resetCallstack();
  94. }
  95. /**
  96. * Resets the performance profiling data
  97. */
  98. public function resetPerformance()
  99. {
  100. $this->performance = [];
  101. $this->performance['start'] = microtime(true);
  102. $this->performance['database'] = 0;
  103. $this->performance['database_write'] = 0;
  104. $this->performance['cache'] = 0;
  105. $this->performance['cache_write'] = 0;
  106. $this->performance['network'] = 0;
  107. $this->performance['file'] = 0;
  108. $this->performance['rendering'] = 0;
  109. $this->performance['parser'] = 0;
  110. $this->performance['marktime'] = 0;
  111. $this->performance['marktime'] = microtime(true);
  112. }
  113. /**
  114. * Resets the callstack profiling data
  115. */
  116. public function resetCallstack()
  117. {
  118. $this->callstack = [];
  119. $this->callstack['database'] = [];
  120. $this->callstack['database_write'] = [];
  121. $this->callstack['cache'] = [];
  122. $this->callstack['cache_write'] = [];
  123. $this->callstack['network'] = [];
  124. $this->callstack['file'] = [];
  125. $this->callstack['rendering'] = [];
  126. $this->callstack['parser'] = [];
  127. }
  128. /**
  129. * Returns the rendertime string
  130. *
  131. * @return string the rendertime
  132. */
  133. public function getRendertimeString()
  134. {
  135. $output = '';
  136. if (!$this->enabled || !$this->rendertime) {
  137. return $output;
  138. }
  139. if (isset($this->callstack["database"])) {
  140. $output .= "\nDatabase Read:\n";
  141. foreach ($this->callstack["database"] as $func => $time) {
  142. $time = round($time, 3);
  143. if ($time > 0) {
  144. $output .= $func . ": " . $time . "\n";
  145. }
  146. }
  147. }
  148. if (isset($this->callstack["database_write"])) {
  149. $output .= "\nDatabase Write:\n";
  150. foreach ($this->callstack["database_write"] as $func => $time) {
  151. $time = round($time, 3);
  152. if ($time > 0) {
  153. $output .= $func . ": " . $time . "\n";
  154. }
  155. }
  156. }
  157. if (isset($this->callstack["cache"])) {
  158. $output .= "\nCache Read:\n";
  159. foreach ($this->callstack["cache"] as $func => $time) {
  160. $time = round($time, 3);
  161. if ($time > 0) {
  162. $output .= $func . ": " . $time . "\n";
  163. }
  164. }
  165. }
  166. if (isset($this->callstack["cache_write"])) {
  167. $output .= "\nCache Write:\n";
  168. foreach ($this->callstack["cache_write"] as $func => $time) {
  169. $time = round($time, 3);
  170. if ($time > 0) {
  171. $output .= $func . ": " . $time . "\n";
  172. }
  173. }
  174. }
  175. if (isset($this->callstack["network"])) {
  176. $output .= "\nNetwork:\n";
  177. foreach ($this->callstack["network"] as $func => $time) {
  178. $time = round($time, 3);
  179. if ($time > 0) {
  180. $output .= $func . ": " . $time . "\n";
  181. }
  182. }
  183. }
  184. return $output;
  185. }
  186. /**
  187. * Save the current profiling data to a log entry
  188. *
  189. * @param LoggerInterface $logger The logger to save the current log
  190. * @param string $message Additional message for the log
  191. */
  192. public function saveLog(LoggerInterface $logger, $message = '')
  193. {
  194. $duration = microtime(true) - $this->get('start');
  195. $logger->info(
  196. $message,
  197. [
  198. 'action' => 'profiling',
  199. 'database_read' => round($this->get('database') - $this->get('database_write'), 3),
  200. 'database_write' => round($this->get('database_write'), 3),
  201. 'cache_read' => round($this->get('cache'), 3),
  202. 'cache_write' => round($this->get('cache_write'), 3),
  203. 'network_io' => round($this->get('network'), 2),
  204. 'file_io' => round($this->get('file'), 2),
  205. 'other_io' => round($duration - ($this->get('database')
  206. + $this->get('cache') + $this->get('cache_write')
  207. + $this->get('network') + $this->get('file')), 2),
  208. 'total' => round($duration, 2)
  209. ]
  210. );
  211. if ($this->isRendertime()) {
  212. $output = $this->getRendertimeString();
  213. $logger->info($message . ": " . $output, ['action' => 'profiling']);
  214. }
  215. }
  216. /**
  217. * Finds an entry of the container by its identifier and returns it.
  218. *
  219. * @param string $id Identifier of the entry to look for.
  220. *
  221. * @throws NotFoundExceptionInterface No entry was found for **this** identifier.
  222. * @throws ContainerExceptionInterface Error while retrieving the entry.
  223. *
  224. * @return int Entry.
  225. */
  226. public function get($id)
  227. {
  228. if (!$this->has($id)) {
  229. return 0;
  230. } else {
  231. return $this->performance[$id];
  232. }
  233. }
  234. /**
  235. * Returns true if the container can return an entry for the given identifier.
  236. * Returns false otherwise.
  237. *
  238. * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
  239. * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
  240. *
  241. * @param string $id Identifier of the entry to look for.
  242. *
  243. * @return bool
  244. */
  245. public function has($id)
  246. {
  247. return isset($this->performance[$id]);
  248. }
  249. }