‪TYPO3CMS  11.5
FileWriter.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
23 
28 {
32  protected string ‪$logFile = '';
33 
34  protected string ‪$logFileInfix = '';
35 
39  protected string ‪$defaultLogFileTemplate = '/log/typo3_%s.log';
40 
49  protected static array ‪$logFileHandles = [];
50 
60  protected static array ‪$logFileHandlesCount = [];
61 
67  public function ‪__construct(array $options = [])
68  {
69  // the parent constructor reads $options and sets them
70  parent::__construct($options);
71  if (empty($options['logFile']) &&
72  // omit logging if TYPO3 has not been configured (avoid creating a guessable filename)
73  (‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] ?? '') !== ''
74  ) {
75  $this->‪setLogFile($this->‪getDefaultLogFileName());
76  }
77  }
78 
82  public function ‪__destruct()
83  {
84  if ($this->logFile === '') {
85  return;
86  }
87  self::$logFileHandlesCount[‪$this->logFile]--;
88  if (self::$logFileHandlesCount[$this->logFile] <= 0) {
89  $this->‪closeLogFile();
90  }
91  }
92 
93  public function ‪setLogFileInfix(string $infix)
94  {
95  $this->logFileInfix = $infix;
96  }
97 
105  public function ‪setLogFile(string $relativeLogFile)
106  {
107  ‪$logFile = $relativeLogFile;
108  // Skip handling if logFile is a stream resource. This is used by unit tests with vfs:// directories
110  ‪$logFile = GeneralUtility::getFileAbsFileName(‪$logFile);
111  if (empty(‪$logFile)) {
113  'Log file path "' . $relativeLogFile . '" is not valid!',
114  1444374805
115  );
116  }
117  }
118  $this->logFile = ‪$logFile;
119  $this->‪openLogFile();
120 
121  return $this;
122  }
123 
127  public function ‪getLogFile(): string
128  {
129  return ‪$this->logFile;
130  }
131 
139  public function ‪writeLog(‪LogRecord $record)
140  {
141  if ($this->logFile === '') {
142  return $this;
143  }
144 
145  $data = '';
146  $context = $record->‪getData();
147  $message = $record->‪getMessage();
148  if (!empty($context)) {
149  // Fold an exception into the message, and string-ify it into context so it can be jsonified.
150  if (isset($context['exception']) && $context['exception'] instanceof \Throwable) {
151  $message .= $this->‪formatException($context['exception']);
152  $context['exception'] = (string)$context['exception'];
153  }
154  $data = '- ' . json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
155  }
156 
157  $message = sprintf(
158  '%s [%s] request="%s" component="%s": %s %s',
159  date('r', (int)$record->‪getCreated()),
160  strtoupper($record->‪getLevel()),
161  $record->‪getRequestId(),
162  $record->‪getComponent(),
163  $this->interpolate($message, $context),
164  $data
165  );
166 
167  if (fwrite(self::$logFileHandles[$this->logFile], $message . LF) === false) {
168  throw new \RuntimeException('Could not write log record to log file', 1345036335);
169  }
170 
171  return $this;
172  }
173 
179  protected function ‪openLogFile()
180  {
181  if (isset(self::$logFileHandlesCount[$this->logFile])) {
182  self::$logFileHandlesCount[‪$this->logFile]++;
183  } else {
184  self::$logFileHandlesCount[‪$this->logFile] = 1;
185  }
186  if (isset(self::$logFileHandles[$this->logFile]) && is_resource(self::$logFileHandles[$this->logFile] ?? false)) {
187  return;
188  }
189 
190  $this->‪createLogFile();
191  self::$logFileHandles[‪$this->logFile] = fopen($this->logFile, 'a');
192  if (!is_resource(self::$logFileHandles[$this->logFile])) {
193  throw new \RuntimeException('Could not open log file "' . $this->logFile . '"', 1321804422);
194  }
195  }
196 
200  protected function ‪closeLogFile()
201  {
202  if (!empty(self::$logFileHandles[$this->logFile]) && is_resource(self::$logFileHandles[$this->logFile])) {
203  fclose(self::$logFileHandles[$this->logFile]);
204  unset(self::$logFileHandles[$this->logFile]);
205  }
206  }
207 
212  protected function ‪createLogFile()
213  {
214  if (file_exists($this->logFile)) {
215  return;
216  }
217 
218  // skip mkdir if logFile refers to any scheme but vfs://, file:// or empty
219  $scheme = parse_url($this->logFile, PHP_URL_SCHEME);
220  if ($scheme === null || $scheme === 'file' || $scheme === 'vfs' || ‪PathUtility::isAbsolutePath($this->logFile)) {
221  // remove file:/ before creating the directory
222  $logFileDirectory = ‪PathUtility::dirname((string)preg_replace('#^file:/#', '', $this->logFile));
223  if (!@is_dir($logFileDirectory)) {
224  ‪GeneralUtility::mkdir_deep($logFileDirectory);
225  // create .htaccess file if log file is within the site path
226  if (‪PathUtility::getCommonPrefix([‪Environment::getPublicPath() . '/', $logFileDirectory]) === (‪Environment::getPublicPath() . '/')) {
227  // only create .htaccess, if we created the directory on our own
228  $this->‪createHtaccessFile($logFileDirectory . '/.htaccess');
229  }
230  }
231  }
232  // create the log file
233  ‪GeneralUtility::writeFile($this->logFile, '');
234  }
235 
241  protected function ‪createHtaccessFile($htaccessFile)
242  {
243  // write .htaccess file to protect the log file
244  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['generateApacheHtaccess']) && !file_exists($htaccessFile)) {
245  $htaccessContent = <<<END
246 # Apache < 2.3
247 <IfModule !mod_authz_core.c>
248  Order allow,deny
249  Deny from all
250  Satisfy All
251 </IfModule>
252 
253 # Apache ≥ 2.3
254 <IfModule mod_authz_core.c>
255  Require all denied
256 </IfModule>
257 END;
258  ‪GeneralUtility::writeFile($htaccessFile, $htaccessContent);
259  }
260  }
261 
269  protected function ‪getDefaultLogFileName()
270  {
271  $namePart = substr(GeneralUtility::hmac($this->defaultLogFileTemplate, 'defaultLogFile'), 0, 10);
272  if ($this->logFileInfix !== '') {
273  $namePart = $this->logFileInfix . '_' . $namePart;
274  }
275  return ‪Environment::getVarPath() . sprintf($this->defaultLogFileTemplate, $namePart);
276  }
277 }
‪TYPO3\CMS\Core\Log\Writer\FileWriter\setLogFileInfix
‪setLogFileInfix(string $infix)
Definition: FileWriter.php:93
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileInfix
‪string $logFileInfix
Definition: FileWriter.php:34
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:206
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static string dirname($path)
Definition: PathUtility.php:251
‪TYPO3\CMS\Core\Log\Writer\FileWriter\__destruct
‪__destruct()
Definition: FileWriter.php:82
‪TYPO3\CMS\Core\Log\Exception\InvalidLogWriterConfigurationException
Definition: InvalidLogWriterConfigurationException.php:23
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$defaultLogFileTemplate
‪string $defaultLogFileTemplate
Definition: FileWriter.php:39
‪TYPO3\CMS\Core\Log\LogRecord\getData
‪array getData()
Definition: LogRecord.php:187
‪TYPO3\CMS\Core\Log\LogRecord\getLevel
‪string getLevel()
Definition: LogRecord.php:165
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep($directory)
Definition: GeneralUtility.php:1908
‪TYPO3\CMS\Core\Log\Writer\FileWriter\getLogFile
‪getLogFile()
Definition: FileWriter.php:127
‪TYPO3\CMS\Core\Log\LogRecord
Definition: LogRecord.php:22
‪TYPO3\CMS\Core\Log\Writer\WriterInterface
Definition: WriterInterface.php:24
‪TYPO3\CMS\Core\Utility\PathUtility\getCommonPrefix
‪static string null getCommonPrefix(array $paths)
Definition: PathUtility.php:171
‪TYPO3\CMS\Core\Log\LogRecord\getMessage
‪string getMessage()
Definition: LogRecord.php:222
‪TYPO3\CMS\Core\Log\LogRecord\getComponent
‪string getComponent()
Definition: LogRecord.php:118
‪TYPO3\CMS\Core\Log\Writer\FileWriter\createLogFile
‪createLogFile()
Definition: FileWriter.php:212
‪TYPO3\CMS\Core\Log\Writer\FileWriter\getDefaultLogFileName
‪string getDefaultLogFileName()
Definition: FileWriter.php:269
‪TYPO3\CMS\Core\Log\Writer\FileWriter
Definition: FileWriter.php:28
‪TYPO3\CMS\Core\Log\Writer
Definition: AbstractWriter.php:16
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFile
‪string $logFile
Definition: FileWriter.php:32
‪TYPO3\CMS\Core\Utility\PathUtility\isAbsolutePath
‪static bool isAbsolutePath($path)
Definition: PathUtility.php:296
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileHandles
‪static array $logFileHandles
Definition: FileWriter.php:49
‪TYPO3\CMS\Core\Log\Writer\AbstractWriter
Definition: AbstractWriter.php:25
‪TYPO3\CMS\Core\Log\Writer\FileWriter\openLogFile
‪openLogFile()
Definition: FileWriter.php:179
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Utility\PathUtility\hasProtocolAndScheme
‪static bool hasProtocolAndScheme(string $path)
Definition: PathUtility.php:463
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:43
‪TYPO3\CMS\Core\Log\Writer\AbstractWriter\formatException
‪formatException(\Throwable $ex)
Definition: AbstractWriter.php:83
‪TYPO3\CMS\Core\Log\LogRecord\getCreated
‪float getCreated()
Definition: LogRecord.php:140
‪TYPO3\CMS\Core\Log\Writer\FileWriter\createHtaccessFile
‪createHtaccessFile($htaccessFile)
Definition: FileWriter.php:241
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileHandlesCount
‪static array $logFileHandlesCount
Definition: FileWriter.php:60
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFile
‪static bool writeFile($file, $content, $changePermissions=false)
Definition: GeneralUtility.php:1722
‪TYPO3\CMS\Core\Log\Writer\FileWriter\__construct
‪__construct(array $options=[])
Definition: FileWriter.php:67
‪TYPO3\CMS\Core\Log\LogRecord\getRequestId
‪string getRequestId()
Definition: LogRecord.php:244
‪TYPO3\CMS\Core\Log\Writer\FileWriter\setLogFile
‪WriterInterface setLogFile(string $relativeLogFile)
Definition: FileWriter.php:105
‪TYPO3\CMS\Core\Log\Writer\FileWriter\closeLogFile
‪closeLogFile()
Definition: FileWriter.php:200
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static string getVarPath()
Definition: Environment.php:218
‪TYPO3\CMS\Core\Log\Writer\FileWriter\writeLog
‪WriterInterface writeLog(LogRecord $record)
Definition: FileWriter.php:139