‪TYPO3CMS  ‪main
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 
65  public function ‪__construct(array $options = [])
66  {
67  // the parent constructor reads $options and sets them
68  parent::__construct($options);
69  if (empty($options['logFile'])) {
70  $this->‪setLogFile($this->‪getDefaultLogFileName());
71  }
72  }
73 
77  public function ‪__destruct()
78  {
79  self::$logFileHandlesCount[‪$this->logFile]--;
80  if (self::$logFileHandlesCount[$this->logFile] <= 0) {
81  $this->‪closeLogFile();
82  }
83  }
84 
85  public function ‪setLogFileInfix(string $infix)
86  {
87  $this->logFileInfix = $infix;
88  }
89 
97  public function ‪setLogFile(string $relativeLogFile)
98  {
99  ‪$logFile = $relativeLogFile;
100  // Skip handling if logFile is a stream resource. This is used by unit tests with vfs:// directories
102  ‪$logFile = GeneralUtility::getFileAbsFileName(‪$logFile);
103  if (empty(‪$logFile)) {
105  'Log file path "' . $relativeLogFile . '" is not valid!',
106  1444374805
107  );
108  }
109  }
110  $this->logFile = ‪$logFile;
111  $this->‪openLogFile();
112 
113  return $this;
114  }
115 
119  public function ‪getLogFile(): string
120  {
121  return ‪$this->logFile;
122  }
123 
132  {
133  $data = '';
134  $context = ‪$record->getData();
135  $message = ‪$record->getMessage();
136  if (!empty($context)) {
137  // Fold an exception into the message, and string-ify it into context so it can be jsonified.
138  if (isset($context['exception']) && $context['exception'] instanceof \Throwable) {
139  $message .= $this->‪formatException($context['exception']);
140  $context['exception'] = (string)$context['exception'];
141  }
142  $data = '- ' . json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
143  }
144 
145  $message = sprintf(
146  '%s [%s] request="%s" component="%s": %s %s',
147  date('r', (int)‪$record->getCreated()),
148  strtoupper(‪$record->getLevel()),
149  ‪$record->getRequestId(),
150  ‪$record->getComponent(),
151  $this->interpolate($message, $context),
152  $data
153  );
154 
155  if (fwrite(self::$logFileHandles[$this->logFile], $message . LF) === false) {
156  throw new \RuntimeException('Could not write log record to log file', 1345036335);
157  }
158 
159  return $this;
160  }
161 
167  protected function ‪openLogFile()
168  {
169  if (isset(self::$logFileHandlesCount[$this->logFile])) {
170  self::$logFileHandlesCount[‪$this->logFile]++;
171  } else {
172  self::$logFileHandlesCount[‪$this->logFile] = 1;
173  }
174  if (isset(self::$logFileHandles[$this->logFile]) && is_resource(self::$logFileHandles[$this->logFile] ?? false)) {
175  return;
176  }
177 
178  $this->‪createLogFile();
179  self::$logFileHandles[‪$this->logFile] = fopen($this->logFile, 'a');
180  if (!is_resource(self::$logFileHandles[$this->logFile])) {
181  throw new \RuntimeException('Could not open log file "' . $this->logFile . '"', 1321804422);
182  }
183  }
184 
188  protected function ‪closeLogFile()
189  {
190  if (!empty(self::$logFileHandles[$this->logFile]) && is_resource(self::$logFileHandles[$this->logFile])) {
191  fclose(self::$logFileHandles[$this->logFile]);
192  unset(self::$logFileHandles[$this->logFile]);
193  }
194  }
195 
200  protected function ‪createLogFile()
201  {
202  if (file_exists($this->logFile)) {
203  return;
204  }
205 
206  // skip mkdir if logFile refers to any scheme but vfs://, file:// or empty
207  $scheme = parse_url($this->logFile, PHP_URL_SCHEME);
208  if ($scheme === null || $scheme === 'file' || $scheme === 'vfs' || ‪PathUtility::isAbsolutePath($this->logFile)) {
209  // remove file:/ before creating the directory
210  $logFileDirectory = ‪PathUtility::dirname((string)preg_replace('#^file:/#', '', $this->logFile));
211  if (!@is_dir($logFileDirectory)) {
212  ‪GeneralUtility::mkdir_deep($logFileDirectory);
213  // create .htaccess file if log file is within the site path
214  if (‪PathUtility::getCommonPrefix([‪Environment::getPublicPath() . '/', $logFileDirectory]) === (‪Environment::getPublicPath() . '/')) {
215  // only create .htaccess, if we created the directory on our own
216  $this->‪createHtaccessFile($logFileDirectory . '/.htaccess');
217  }
218  }
219  }
220  // create the log file
221  ‪GeneralUtility::writeFile($this->logFile, '');
222  }
223 
229  protected function ‪createHtaccessFile($htaccessFile)
230  {
231  // write .htaccess file to protect the log file
232  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['generateApacheHtaccess']) && !file_exists($htaccessFile)) {
233  $htaccessContent = <<<END
234 # Apache < 2.3
235 <IfModule !mod_authz_core.c>
236  Order allow,deny
237  Deny from all
238  Satisfy All
239 </IfModule>
240 
241 # Apache ≥ 2.3
242 <IfModule mod_authz_core.c>
243  Require all denied
244 </IfModule>
245 END;
246  ‪GeneralUtility::writeFile($htaccessFile, $htaccessContent);
247  }
248  }
249 
257  protected function ‪getDefaultLogFileName()
258  {
259  $namePart = substr(‪GeneralUtility::hmac($this->defaultLogFileTemplate, 'defaultLogFile'), 0, 10);
260  if ($this->logFileInfix !== '') {
261  $namePart = $this->logFileInfix . '_' . $namePart;
262  }
263  return ‪Environment::getVarPath() . sprintf($this->defaultLogFileTemplate, $namePart);
264  }
265 }
‪TYPO3\CMS\Core\Log\Writer\FileWriter\setLogFileInfix
‪setLogFileInfix(string $infix)
Definition: FileWriter.php:85
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileInfix
‪string $logFileInfix
Definition: FileWriter.php:34
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Utility\PathUtility\isAbsolutePath
‪static isAbsolutePath(string $path)
Definition: PathUtility.php:286
‪TYPO3\CMS\Core\Log\Writer\FileWriter\__destruct
‪__destruct()
Definition: FileWriter.php:77
‪TYPO3\CMS\Core\Log\Exception\InvalidLogWriterConfigurationException
Definition: InvalidLogWriterConfigurationException.php:24
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$defaultLogFileTemplate
‪string $defaultLogFileTemplate
Definition: FileWriter.php:39
‪TYPO3\CMS\Core\Utility\PathUtility\getCommonPrefix
‪static getCommonPrefix(array $paths)
Definition: PathUtility.php:165
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static getVarPath()
Definition: Environment.php:197
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static dirname(string $path)
Definition: PathUtility.php:243
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep($directory)
Definition: GeneralUtility.php:1638
‪TYPO3\CMS\Core\Log\Writer\FileWriter\getLogFile
‪getLogFile()
Definition: FileWriter.php:119
‪TYPO3\CMS\Core\Log\LogRecord
Definition: LogRecord.php:24
‪TYPO3\CMS\Core\Log\Writer\WriterInterface
Definition: WriterInterface.php:24
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Core\Log\Writer\FileWriter\createLogFile
‪createLogFile()
Definition: FileWriter.php:200
‪TYPO3\CMS\Core\Log\Writer\FileWriter\getDefaultLogFileName
‪string getDefaultLogFileName()
Definition: FileWriter.php:257
‪TYPO3\CMS\Core\Utility\GeneralUtility\hmac
‪static string hmac($input, $additionalSecret='')
Definition: GeneralUtility.php:475
‪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\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:167
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Log\Writer\AbstractWriter\formatException
‪formatException(\Throwable $ex)
Definition: AbstractWriter.php:82
‪TYPO3\CMS\Core\Log\Writer\FileWriter\createHtaccessFile
‪createHtaccessFile($htaccessFile)
Definition: FileWriter.php:229
‪TYPO3\CMS\Core\Utility\PathUtility\hasProtocolAndScheme
‪static hasProtocolAndScheme(string $path)
Definition: PathUtility.php:445
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileHandlesCount
‪static array $logFileHandlesCount
Definition: FileWriter.php:60
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFile
‪static bool writeFile($file, $content, $changePermissions=false)
Definition: GeneralUtility.php:1452
‪TYPO3\CMS\Core\Log\Writer\FileWriter\__construct
‪__construct(array $options=[])
Definition: FileWriter.php:65
‪TYPO3\CMS\Core\Log\Writer\FileWriter\setLogFile
‪WriterInterface setLogFile(string $relativeLogFile)
Definition: FileWriter.php:97
‪TYPO3\CMS\Core\Log\Writer\FileWriter\closeLogFile
‪closeLogFile()
Definition: FileWriter.php:188
‪TYPO3\CMS\Core\Log\Writer\FileWriter\writeLog
‪WriterInterface writeLog(LogRecord $record)
Definition: FileWriter.php:131