[!!!][FEATURE] Introduce PSR-3 Logging
[Packages/TYPO3.CMS.git] / typo3 / sysext / adminpanel / Classes / Modules / Debug / Log.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 Psr\Http\Message\ServerRequestInterface;
20 use TYPO3\CMS\Adminpanel\Log\InMemoryLogWriter;
21 use TYPO3\CMS\Adminpanel\ModuleApi\AbstractSubModule;
22 use TYPO3\CMS\Adminpanel\ModuleApi\ContentProviderInterface;
23 use TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface;
24 use TYPO3\CMS\Adminpanel\ModuleApi\InitializableInterface;
25 use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
26 use TYPO3\CMS\Adminpanel\ModuleApi\ModuleSettingsProviderInterface;
27 use TYPO3\CMS\Adminpanel\Service\ConfigurationService;
28 use TYPO3\CMS\Core\Log\LogLevel;
29 use TYPO3\CMS\Core\Utility\GeneralUtility;
30 use TYPO3\CMS\Fluid\View\StandaloneView;
31
32 /**
33 * Log Sub Module of the AdminPanel
34 *
35 * @internal
36 */
37 class Log extends AbstractSubModule implements DataProviderInterface, ContentProviderInterface, ModuleSettingsProviderInterface, InitializableInterface
38 {
39 /**
40 * @var int
41 */
42 protected $logLevel;
43
44 /**
45 * @var ConfigurationService
46 */
47 protected $configurationService;
48
49 public function __construct()
50 {
51 $this->logLevel = LogLevel::normalizeLevel(LogLevel::INFO);
52 $this->configurationService = GeneralUtility::makeInstance(ConfigurationService::class);
53 }
54
55 /**
56 * @return string
57 */
58 public function getIdentifier(): string
59 {
60 return 'debug_log';
61 }
62
63 /**
64 * Sub-Module label
65 *
66 * @return string
67 */
68 public function getLabel(): string
69 {
70 return $this->getLanguageService()->sL(
71 'LLL:EXT:adminpanel/Resources/Private/Language/locallang_debug.xlf:submodule.log.label'
72 );
73 }
74
75 /**
76 * @inheritdoc
77 */
78 public function getDataToStore(ServerRequestInterface $request): ModuleData
79 {
80 $maxLevel = LogLevel::normalizeLevel(LogLevel::DEBUG);
81 $levels = [];
82 for ($i = 1; $i <= $maxLevel; $i++) {
83 $levels[] = [
84 'level' => $i,
85 'levelName' => LogLevel::getName($i),
86 ];
87 }
88
89 $log = InMemoryLogWriter::$log;
90
91 $logArray = [];
92 /** @var \TYPO3\CMS\Core\Log\LogRecord $logRecord */
93 foreach ($log as $logRecord) {
94 $entry = $logRecord->toArray();
95 // store only necessary info
96 unset($entry['data']);
97 $logArray[] = $entry;
98 }
99 return new ModuleData(
100 [
101 'levels' => $levels,
102 'startLevel' => (int)$this->getConfigOption('startLevel'),
103 'log' => $logArray,
104 ]
105 );
106 }
107
108 /**
109 * @inheritdoc
110 */
111 public function getSettings(): string
112 {
113 $view = GeneralUtility::makeInstance(StandaloneView::class);
114 $templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Debug/LogSettings.html';
115 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
116 $view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
117
118 $maxLevel = LogLevel::normalizeLevel(LogLevel::DEBUG);
119 $levels = [];
120 for ($i = 1; $i <= $maxLevel; $i++) {
121 $levels[] = [
122 'level' => $i,
123 'levelName' => LogLevel::getName($i),
124 ];
125 }
126 $view->assignMultiple(
127 [
128 'levels' => $levels,
129 'startLevel' => (int)$this->getConfigOption('startLevel'),
130 'groupByComponent' => $this->getConfigOption('groupByComponent'),
131 'groupByLevel' => $this->getConfigOption('groupByLevel'),
132 ]
133 );
134
135 return $view->render();
136 }
137
138 /**
139 * Sub-Module content as rendered HTML
140 *
141 * @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleData $data
142 * @return string
143 */
144 public function getContent(ModuleData $data): string
145 {
146 $this->logLevel = $this->getConfigOption('startLevel');
147 $view = GeneralUtility::makeInstance(StandaloneView::class);
148 $templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Debug/Log.html';
149 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
150 $view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
151 $sortedLog = [];
152 // settings for this module
153 $groupByComponent = $this->getConfigOption('groupByComponent');
154 $groupByLevel = $this->getConfigOption('groupByLevel');
155
156 foreach ($data['log'] as $logRecord) {
157 if ($logRecord['level'] > $this->logLevel) {
158 continue;
159 }
160 if ($groupByComponent && $groupByLevel) {
161 $sortedLog[$logRecord['component']][LogLevel::getName($logRecord['level'])][] = $logRecord;
162 } elseif ($groupByComponent) {
163 $sortedLog[$logRecord['component']][] = $logRecord;
164 } elseif ($groupByLevel) {
165 $sortedLog[$logRecord['level']][] = $logRecord;
166 } else {
167 $sortedLog[] = $logRecord;
168 }
169 }
170 $data['log'] = $sortedLog;
171 $data['groupByComponent'] = $groupByComponent;
172 $data['groupByLevel'] = $groupByLevel;
173 $view->assignMultiple($data->getArrayCopy());
174
175 return $view->render();
176 }
177
178 /**
179 * @inheritdoc
180 */
181 public function initializeModule(ServerRequestInterface $request): void
182 {
183 $this->logLevel = $this->getConfigOption('startLevel');
184
185 // set inMemoryLogWriter recursively for all configured namespaces/areas so we don't lose log entries
186 $configWithInMemoryWriter = $this->setLoggingConfigRecursive($GLOBALS['TYPO3_CONF_VARS']['LOG'] ?? []);
187
188 // in case there are empty array parts, remove them
189 $GLOBALS['TYPO3_CONF_VARS']['LOG'] = array_filter(
190 $configWithInMemoryWriter
191 );
192 }
193
194 protected function setLoggingConfigRecursive(array $logConfig): array
195 {
196 $maxLevel = LogLevel::normalizeLevel(LogLevel::DEBUG);
197 foreach ($logConfig as $key => $value) {
198 if ($key === 'writerConfiguration') {
199 $logConfig[$key] = $value;
200 $logConfig[$key][$maxLevel][InMemoryLogWriter::class] = [];
201 } elseif (is_array($value)) {
202 $logConfig[$key] = $this->setLoggingConfigRecursive($value);
203 }
204 }
205 return $logConfig;
206 }
207
208 /**
209 * @param string $option
210 * @return string
211 */
212 protected function getConfigOption(string $option): string
213 {
214 return $this->configurationService->getConfigurationOption('debug_log', $option);
215 }
216 }