PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php

https://gitlab.com/jjpa2018/dashboard
PHP | 357 lines | 272 code | 58 blank | 27 comment | 35 complexity | cb9058b5b512ddbf8afacbc65f8c9fe7 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpKernel\DataCollector;
  11. use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\RequestStack;
  14. use Symfony\Component\HttpFoundation\Response;
  15. use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
  16. /**
  17. * @author Fabien Potencier <fabien@symfony.com>
  18. *
  19. * @final
  20. */
  21. class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface
  22. {
  23. private $logger;
  24. private $containerPathPrefix;
  25. private $currentRequest;
  26. private $requestStack;
  27. private $processedLogs;
  28. public function __construct(object $logger = null, string $containerPathPrefix = null, RequestStack $requestStack = null)
  29. {
  30. if (null !== $logger && $logger instanceof DebugLoggerInterface) {
  31. $this->logger = $logger;
  32. }
  33. $this->containerPathPrefix = $containerPathPrefix;
  34. $this->requestStack = $requestStack;
  35. }
  36. /**
  37. * {@inheritdoc}
  38. */
  39. public function collect(Request $request, Response $response, \Throwable $exception = null)
  40. {
  41. $this->currentRequest = $this->requestStack && $this->requestStack->getMainRequest() !== $request ? $request : null;
  42. }
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public function reset()
  47. {
  48. if ($this->logger instanceof DebugLoggerInterface) {
  49. $this->logger->clear();
  50. }
  51. $this->data = [];
  52. }
  53. /**
  54. * {@inheritdoc}
  55. */
  56. public function lateCollect()
  57. {
  58. if (null !== $this->logger) {
  59. $containerDeprecationLogs = $this->getContainerDeprecationLogs();
  60. $this->data = $this->computeErrorsCount($containerDeprecationLogs);
  61. // get compiler logs later (only when they are needed) to improve performance
  62. $this->data['compiler_logs'] = [];
  63. $this->data['compiler_logs_filepath'] = $this->containerPathPrefix.'Compiler.log';
  64. $this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs($this->currentRequest), $containerDeprecationLogs));
  65. $this->data = $this->cloneVar($this->data);
  66. }
  67. $this->currentRequest = null;
  68. }
  69. public function getLogs()
  70. {
  71. return $this->data['logs'] ?? [];
  72. }
  73. public function getProcessedLogs()
  74. {
  75. if (null !== $this->processedLogs) {
  76. return $this->processedLogs;
  77. }
  78. $rawLogs = $this->getLogs();
  79. if ([] === $rawLogs) {
  80. return $this->processedLogs = $rawLogs;
  81. }
  82. $logs = [];
  83. foreach ($this->getLogs()->getValue() as $rawLog) {
  84. $rawLogData = $rawLog->getValue();
  85. if ($rawLogData['priority']->getValue() > 300) {
  86. $logType = 'error';
  87. } elseif (isset($rawLogData['scream']) && false === $rawLogData['scream']->getValue()) {
  88. $logType = 'deprecation';
  89. } elseif (isset($rawLogData['scream']) && true === $rawLogData['scream']->getValue()) {
  90. $logType = 'silenced';
  91. } else {
  92. $logType = 'regular';
  93. }
  94. $logs[] = [
  95. 'type' => $logType,
  96. 'errorCounter' => isset($rawLogData['errorCounter']) ? $rawLogData['errorCounter']->getValue() : 1,
  97. 'timestamp' => $rawLogData['timestamp_rfc3339']->getValue(),
  98. 'priority' => $rawLogData['priority']->getValue(),
  99. 'priorityName' => $rawLogData['priorityName']->getValue(),
  100. 'channel' => $rawLogData['channel']->getValue(),
  101. 'message' => $rawLogData['message'],
  102. 'context' => $rawLogData['context'],
  103. ];
  104. }
  105. // sort logs from oldest to newest
  106. usort($logs, static function ($logA, $logB) {
  107. return $logA['timestamp'] <=> $logB['timestamp'];
  108. });
  109. return $this->processedLogs = $logs;
  110. }
  111. public function getFilters()
  112. {
  113. $filters = [
  114. 'channel' => [],
  115. 'priority' => [
  116. 'Debug' => 100,
  117. 'Info' => 200,
  118. 'Warning' => 300,
  119. 'Error' => 400,
  120. 'Critical' => 500,
  121. 'Alert' => 550,
  122. 'Emergency' => 600,
  123. ],
  124. ];
  125. $allChannels = [];
  126. foreach ($this->getProcessedLogs() as $log) {
  127. if ('' === trim($log['channel'])) {
  128. continue;
  129. }
  130. $allChannels[] = $log['channel'];
  131. }
  132. $channels = array_unique($allChannels);
  133. sort($channels);
  134. $filters['channel'] = $channels;
  135. return $filters;
  136. }
  137. public function getPriorities()
  138. {
  139. return $this->data['priorities'] ?? [];
  140. }
  141. public function countErrors()
  142. {
  143. return $this->data['error_count'] ?? 0;
  144. }
  145. public function countDeprecations()
  146. {
  147. return $this->data['deprecation_count'] ?? 0;
  148. }
  149. public function countWarnings()
  150. {
  151. return $this->data['warning_count'] ?? 0;
  152. }
  153. public function countScreams()
  154. {
  155. return $this->data['scream_count'] ?? 0;
  156. }
  157. public function getCompilerLogs()
  158. {
  159. return $this->cloneVar($this->getContainerCompilerLogs($this->data['compiler_logs_filepath'] ?? null));
  160. }
  161. /**
  162. * {@inheritdoc}
  163. */
  164. public function getName(): string
  165. {
  166. return 'logger';
  167. }
  168. private function getContainerDeprecationLogs(): array
  169. {
  170. if (null === $this->containerPathPrefix || !is_file($file = $this->containerPathPrefix.'Deprecations.log')) {
  171. return [];
  172. }
  173. if ('' === $logContent = trim(file_get_contents($file))) {
  174. return [];
  175. }
  176. $bootTime = filemtime($file);
  177. $logs = [];
  178. foreach (unserialize($logContent) as $log) {
  179. $log['context'] = ['exception' => new SilencedErrorContext($log['type'], $log['file'], $log['line'], $log['trace'], $log['count'])];
  180. $log['timestamp'] = $bootTime;
  181. $log['timestamp_rfc3339'] = (new \DateTimeImmutable())->setTimestamp($bootTime)->format(\DateTimeInterface::RFC3339_EXTENDED);
  182. $log['priority'] = 100;
  183. $log['priorityName'] = 'DEBUG';
  184. $log['channel'] = null;
  185. $log['scream'] = false;
  186. unset($log['type'], $log['file'], $log['line'], $log['trace'], $log['trace'], $log['count']);
  187. $logs[] = $log;
  188. }
  189. return $logs;
  190. }
  191. private function getContainerCompilerLogs(string $compilerLogsFilepath = null): array
  192. {
  193. if (!is_file($compilerLogsFilepath)) {
  194. return [];
  195. }
  196. $logs = [];
  197. foreach (file($compilerLogsFilepath, \FILE_IGNORE_NEW_LINES) as $log) {
  198. $log = explode(': ', $log, 2);
  199. if (!isset($log[1]) || !preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $log[0])) {
  200. $log = ['Unknown Compiler Pass', implode(': ', $log)];
  201. }
  202. $logs[$log[0]][] = ['message' => $log[1]];
  203. }
  204. return $logs;
  205. }
  206. private function sanitizeLogs(array $logs)
  207. {
  208. $sanitizedLogs = [];
  209. $silencedLogs = [];
  210. foreach ($logs as $log) {
  211. if (!$this->isSilencedOrDeprecationErrorLog($log)) {
  212. $sanitizedLogs[] = $log;
  213. continue;
  214. }
  215. $message = '_'.$log['message'];
  216. $exception = $log['context']['exception'];
  217. if ($exception instanceof SilencedErrorContext) {
  218. if (isset($silencedLogs[$h = spl_object_hash($exception)])) {
  219. continue;
  220. }
  221. $silencedLogs[$h] = true;
  222. if (!isset($sanitizedLogs[$message])) {
  223. $sanitizedLogs[$message] = $log + [
  224. 'errorCount' => 0,
  225. 'scream' => true,
  226. ];
  227. }
  228. $sanitizedLogs[$message]['errorCount'] += $exception->count;
  229. continue;
  230. }
  231. $errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}\0{$message}", true);
  232. if (isset($sanitizedLogs[$errorId])) {
  233. ++$sanitizedLogs[$errorId]['errorCount'];
  234. } else {
  235. $log += [
  236. 'errorCount' => 1,
  237. 'scream' => false,
  238. ];
  239. $sanitizedLogs[$errorId] = $log;
  240. }
  241. }
  242. return array_values($sanitizedLogs);
  243. }
  244. private function isSilencedOrDeprecationErrorLog(array $log): bool
  245. {
  246. if (!isset($log['context']['exception'])) {
  247. return false;
  248. }
  249. $exception = $log['context']['exception'];
  250. if ($exception instanceof SilencedErrorContext) {
  251. return true;
  252. }
  253. if ($exception instanceof \ErrorException && \in_array($exception->getSeverity(), [\E_DEPRECATED, \E_USER_DEPRECATED], true)) {
  254. return true;
  255. }
  256. return false;
  257. }
  258. private function computeErrorsCount(array $containerDeprecationLogs): array
  259. {
  260. $silencedLogs = [];
  261. $count = [
  262. 'error_count' => $this->logger->countErrors($this->currentRequest),
  263. 'deprecation_count' => 0,
  264. 'warning_count' => 0,
  265. 'scream_count' => 0,
  266. 'priorities' => [],
  267. ];
  268. foreach ($this->logger->getLogs($this->currentRequest) as $log) {
  269. if (isset($count['priorities'][$log['priority']])) {
  270. ++$count['priorities'][$log['priority']]['count'];
  271. } else {
  272. $count['priorities'][$log['priority']] = [
  273. 'count' => 1,
  274. 'name' => $log['priorityName'],
  275. ];
  276. }
  277. if ('WARNING' === $log['priorityName']) {
  278. ++$count['warning_count'];
  279. }
  280. if ($this->isSilencedOrDeprecationErrorLog($log)) {
  281. $exception = $log['context']['exception'];
  282. if ($exception instanceof SilencedErrorContext) {
  283. if (isset($silencedLogs[$h = spl_object_hash($exception)])) {
  284. continue;
  285. }
  286. $silencedLogs[$h] = true;
  287. $count['scream_count'] += $exception->count;
  288. } else {
  289. ++$count['deprecation_count'];
  290. }
  291. }
  292. }
  293. foreach ($containerDeprecationLogs as $deprecationLog) {
  294. $count['deprecation_count'] += $deprecationLog['context']['exception']->count;
  295. }
  296. ksort($count['priorities']);
  297. return $count;
  298. }
  299. }