[!!!][FEATURE] Introduce PSR-3 Logging
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Log / Logger.php
1 <?php
2 namespace TYPO3\CMS\Core\Log;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Log\AbstractLogger;
18 use TYPO3\CMS\Core\Log\Processor\ProcessorInterface;
19 use TYPO3\CMS\Core\Log\Writer\WriterInterface;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21
22 /**
23 * Logger to log events and data for different components.
24 */
25 class Logger extends AbstractLogger
26 {
27 /**
28 * Logger name or component for which this logger is meant to be used for.
29 * This should be a dot-separated name and should normally be based on
30 * the class name or the name of a subsystem, such as
31 * core.t3lib.cache.manager, core.backend.workspaces or extension.news
32 *
33 * @var string
34 */
35 protected $name = '';
36
37 /**
38 * Unique ID of the request
39 *
40 * @var string
41 */
42 protected $requestId = '';
43
44 /**
45 * Minimum log level, anything below this level will be ignored.
46 *
47 * @var int
48 */
49 protected $minimumLogLevel;
50
51 /**
52 * Writers used by this logger
53 *
54 * @var array
55 */
56 protected $writers = [];
57
58 /**
59 * Processors used by this logger
60 *
61 * @var array
62 */
63 protected $processors = [];
64
65 /**
66 * Constructor.
67 *
68 * @param string $name A name for the logger.
69 * @param string $requestId Unique ID of the request
70 */
71 public function __construct(string $name, string $requestId = '')
72 {
73 $this->name = $name;
74 $this->requestId = $requestId;
75 $this->minimumLogLevel = LogLevel::normalizeLevel(LogLevel::EMERGENCY);
76 }
77
78 /**
79 * Re-initialize instance with creating a new instance with up to date information
80 */
81 public function __wakeup()
82 {
83 $newLogger = GeneralUtility::makeInstance(LogManager::class)->getLogger($this->name);
84 $this->requestId = $newLogger->requestId;
85 $this->minimumLogLevel = $newLogger->minimumLogLevel;
86 $this->writers = $newLogger->writers;
87 $this->processors = $newLogger->processors;
88 }
89
90 /**
91 * Remove everything except the name, to be able to restore it on wakeup
92 *
93 * @return array
94 */
95 public function __sleep(): array
96 {
97 return ['name'];
98 }
99
100 /**
101 * Sets the minimum log level for which log records are written.
102 *
103 * @param int $level Minimum log level
104 * @return \TYPO3\CMS\Core\Log\Logger $this
105 */
106 protected function setMinimumLogLevel($level)
107 {
108 LogLevel::validateLevel($level);
109 $this->minimumLogLevel = $level;
110 return $this;
111 }
112
113 /**
114 * Gets the minimum log level for which log records are written.
115 *
116 * @return int Minimum log level
117 */
118 protected function getMinimumLogLevel()
119 {
120 return $this->minimumLogLevel;
121 }
122
123 /**
124 * Gets the logger's name.
125 *
126 * @return string Logger name.
127 */
128 public function getName()
129 {
130 return $this->name;
131 }
132
133 /**
134 * Adds a writer to this logger
135 *
136 * @param string $minimumLevel
137 * @param \TYPO3\CMS\Core\Log\Writer\WriterInterface $writer Writer object
138 * @return \TYPO3\CMS\Core\Log\Logger $this
139 */
140 public function addWriter(string $minimumLevel, Writer\WriterInterface $writer)
141 {
142 $minLevelAsNumber = LogLevel::normalizeLevel($minimumLevel);
143 LogLevel::validateLevel($minLevelAsNumber);
144 // Cycle through all the log levels which are as severe as or higher
145 // than $minimumLevel and add $writer to each severity level
146 for ($logLevelWhichTriggersWriter = LogLevel::normalizeLevel(LogLevel::EMERGENCY); $logLevelWhichTriggersWriter <= $minLevelAsNumber; $logLevelWhichTriggersWriter++) {
147 $logLevelName = LogLevel::getInternalName($logLevelWhichTriggersWriter);
148 if (!isset($this->writers[$logLevelName])) {
149 $this->writers[$logLevelName] = [];
150 }
151 $this->writers[$logLevelName][] = $writer;
152 }
153 if ($minLevelAsNumber > $this->getMinimumLogLevel()) {
154 $this->setMinimumLogLevel($minLevelAsNumber);
155 }
156 return $this;
157 }
158
159 /**
160 * Returns all configured writers indexed by log level
161 *
162 * @return array
163 */
164 public function getWriters()
165 {
166 return $this->writers;
167 }
168
169 /**
170 * Adds a processor to the logger.
171 *
172 * @param string $minimumLevel
173 * @param \TYPO3\CMS\Core\Log\Processor\ProcessorInterface $processor The processor to add.
174 */
175 public function addProcessor(string $minimumLevel, Processor\ProcessorInterface $processor)
176 {
177 $minLevelAsNumber = LogLevel::normalizeLevel($minimumLevel);
178 LogLevel::validateLevel($minLevelAsNumber);
179 // Cycle through all the log levels which are as severe as or higher
180 // than $minimumLevel and add $processor to each severity level
181 for ($logLevelWhichTriggersProcessor = LogLevel::normalizeLevel(LogLevel::EMERGENCY); $logLevelWhichTriggersProcessor <= $minLevelAsNumber; $logLevelWhichTriggersProcessor++) {
182 $logLevelName = LogLevel::getInternalName($logLevelWhichTriggersProcessor);
183 if (!isset($this->processors[$logLevelName])) {
184 $this->processors[$logLevelName] = [];
185 }
186 $this->processors[$logLevelName][] = $processor;
187 }
188 if ($minLevelAsNumber > $this->getMinimumLogLevel()) {
189 $this->setMinimumLogLevel($minLevelAsNumber);
190 }
191 }
192
193 /**
194 * Returns all added processors indexed by log level
195 *
196 * @return array
197 */
198 public function getProcessors()
199 {
200 return $this->processors;
201 }
202
203 /**
204 * Adds a log record.
205 *
206 * @param int|string $level Log level. Value according to \TYPO3\CMS\Core\Log\LogLevel. Alternatively accepts a string.
207 * @param string $message Log message.
208 * @param array $data Additional data to log
209 * @return mixed
210 */
211 public function log($level, $message, array $data = [])
212 {
213 $level = LogLevel::normalizeLevel($level);
214 LogLevel::validateLevel($level);
215 if ($level > $this->minimumLogLevel) {
216 return $this;
217 }
218 /** @var \TYPO3\CMS\Core\Log\LogRecord $record */
219 $record = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(LogRecord::class, $this->name, LogLevel::getInternalName($level), $message, $data, $this->requestId);
220 $record = $this->callProcessors($record);
221 $this->writeLog($record);
222 return $this;
223 }
224
225 /**
226 * Calls all processors and returns log record
227 *
228 * @param \TYPO3\CMS\Core\Log\LogRecord $record Record to process
229 * @throws \RuntimeException
230 * @return \TYPO3\CMS\Core\Log\LogRecord Processed log record
231 */
232 protected function callProcessors(LogRecord $record)
233 {
234 if (!empty($this->processors[$record->getLevel()])) {
235 /** @var ProcessorInterface $processor */
236 foreach ($this->processors[$record->getLevel()] as $processor) {
237 $processedRecord = $processor->processLogRecord($record);
238 if (!$processedRecord instanceof LogRecord) {
239 throw new \RuntimeException('Processor ' . get_class($processor) . ' returned invalid data. Instance of TYPO3\\CMS\\Core\\Log\\LogRecord expected', 1343593398);
240 }
241 $record = $processedRecord;
242 }
243 }
244 return $record;
245 }
246
247 /**
248 * Passes the \TYPO3\CMS\Core\Log\LogRecord to all registered writers.
249 *
250 * @param \TYPO3\CMS\Core\Log\LogRecord $record
251 */
252 protected function writeLog(LogRecord $record)
253 {
254 if (!empty($this->writers[$record->getLevel()])) {
255 /** @var WriterInterface $writer */
256 foreach ($this->writers[$record->getLevel()] as $writer) {
257 $writer->writeLog($record);
258 }
259 }
260 }
261 }