[FEATURE] Add SQL Logging to AdminPanel
[Packages/TYPO3.CMS.git] / typo3 / sysext / adminpanel / Classes / Modules / Debug / QueryInformation.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Adminpanel\Modules\Debug;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use TYPO3\CMS\Adminpanel\Log\DoctrineSqlLogger;
20 use TYPO3\CMS\Adminpanel\Modules\AbstractSubModule;
21 use TYPO3\CMS\Core\Database\ConnectionPool;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Fluid\View\StandaloneView;
24
25 /**
26 * Admin Panel Query Information module for showing SQL Queries
27 */
28 class QueryInformation extends AbstractSubModule
29 {
30 /**
31 * Identifier for this Sub-module,
32 * for example "preview" or "cache"
33 *
34 * @return string
35 */
36 public function getIdentifier(): string
37 {
38 return 'debug_queryinformation';
39 }
40
41 /**
42 * Sub-Module label
43 *
44 * @return string
45 */
46 public function getLabel(): string
47 {
48 return $this->getLanguageService()->sL(
49 'LLL:EXT:adminpanel/Resources/Private/Language/locallang_debug.xlf:submodule.queryInformation.label'
50 );
51 }
52
53 /**
54 * @return string Returns content of admin panel
55 * @throws \UnexpectedValueException
56 * @throws \InvalidArgumentException
57 * @throws \Doctrine\DBAL\DBALException
58 */
59 public function getContent(): string
60 {
61 $view = new StandaloneView();
62 $view->setTemplatePathAndFilename(
63 'typo3/sysext/adminpanel/Resources/Private/Templates/Modules/Debug/QueryInformation.html'
64 );
65 $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
66 $connection = $connectionPool->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME);
67 $logger = $connection->getConfiguration()->getSQLLogger();
68 $this->getLanguageService()->includeLLFile('EXT:adminpanel/Resources/Private/Language/locallang_debug.xlf');
69 if ($logger instanceof DoctrineSqlLogger) {
70 $queries = $logger->getQueries();
71 $this->queryCount = \count($queries);
72 $groupedQueries = $this->groupQueries($queries);
73 $totalTime = array_sum(array_column($queries, 'executionMS')) * 1000;
74 $view->assign('queries', $groupedQueries ?? [])->assign('totalTime', $totalTime);
75 }
76 return $view->render();
77 }
78
79 /**
80 * @param array $queries
81 * @return array
82 */
83 protected function groupQueries(array $queries): array
84 {
85 $groupedQueries = [];
86 foreach ($queries as $query) {
87 $identifier = sha1($query['sql']) . sha1(implode(',', $query['backtrace']));
88 if (is_array($query['params'])) {
89 foreach ($query['params'] as $k => $param) {
90 if (is_array($param)) {
91 $query['params'][$k] = implode(',', $param);
92 }
93 }
94 }
95 if (isset($groupedQueries[$identifier])) {
96 $groupedQueries[$identifier]['count']++;
97 $groupedQueries[$identifier]['time'] += ($query['executionMS'] * 1000);
98 $groupedQueries[$identifier]['queries'][] = $query;
99 } else {
100 $groupedQueries[$identifier] = [
101 'sql' => $query['sql'],
102 'time' => $query['executionMS'] * 1000,
103 'count' => 1,
104 'queries' => [
105 $query,
106 ],
107 ];
108 }
109 }
110 uasort(
111 $groupedQueries,
112 function ($a, $b) {
113 return $b['time'] <=> $a['time'];
114 }
115 );
116 return $groupedQueries;
117 }
118 }