‪TYPO3CMS  10.4
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 {
34  protected ‪$logFile = '';
35 
39  protected ‪$logFileInfix = '';
40 
46  protected ‪$defaultLogFileTemplate = '/log/typo3_%s.log';
47 
57  protected static ‪$logFileHandles = [];
58 
69  protected static ‪$logFileHandlesCount = [];
70 
76  public function ‪__construct(array $options = [])
77  {
78  // the parent constructor reads $options and sets them
79  parent::__construct($options);
80  if (empty($options['logFile'])) {
81  $this->‪setLogFile($this->‪getDefaultLogFileName());
82  }
83  }
84 
88  public function ‪__destruct()
89  {
90  self::$logFileHandlesCount[‪$this->logFile]--;
91  if (self::$logFileHandlesCount[$this->logFile] <= 0) {
92  $this->‪closeLogFile();
93  }
94  }
95 
96  public function ‪setLogFileInfix(string $infix)
97  {
98  $this->logFileInfix = $infix;
99  }
100 
108  public function ‪setLogFile($relativeLogFile)
109  {
110  ‪$logFile = $relativeLogFile;
111  // Skip handling if logFile is a stream resource. This is used by unit tests with vfs:// directories
112  if (false === strpos(‪$logFile, '://') && !‪PathUtility::isAbsolutePath(‪$logFile)) {
113  ‪$logFile = GeneralUtility::getFileAbsFileName(‪$logFile);
114  if (empty(‪$logFile)) {
115  throw new InvalidLogWriterConfigurationException(
116  'Log file path "' . $relativeLogFile . '" is not valid!',
117  1444374805
118  );
119  }
120  }
121  $this->logFile = ‪$logFile;
122  $this->‪openLogFile();
123 
124  return $this;
125  }
126 
132  public function ‪getLogFile()
133  {
134  return ‪$this->logFile;
135  }
136 
144  public function ‪writeLog(LogRecord $record)
145  {
146  $timestamp = date('r', (int)$record->getCreated());
147  $levelName = strtoupper($record->getLevel());
148  $data = '';
149  $recordData = $record->getData();
150  if (!empty($recordData)) {
151  // According to PSR3 the exception-key may hold an \Exception
152  // Since json_encode() does not encode an exception, we run the _toString() here
153  if (isset($recordData['exception']) && $recordData['exception'] instanceof \Exception) {
154  $recordData['exception'] = (string)$recordData['exception'];
155  }
156  $data = '- ' . json_encode($recordData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
157  }
158 
159  $message = sprintf(
160  '%s [%s] request="%s" component="%s": %s %s',
161  $timestamp,
162  $levelName,
163  $record->getRequestId(),
164  $record->getComponent(),
165  $record->getMessage(),
166  $data
167  );
168 
169  if (false === fwrite(self::$logFileHandles[$this->logFile], $message . LF)) {
170  throw new \RuntimeException('Could not write log record to log file', 1345036335);
171  }
172 
173  return $this;
174  }
175 
181  protected function ‪openLogFile()
182  {
183  if (isset(self::$logFileHandlesCount[$this->logFile])) {
184  self::$logFileHandlesCount[‪$this->logFile]++;
185  } else {
186  self::$logFileHandlesCount[‪$this->logFile] = 1;
187  }
188  if (isset(self::$logFileHandles[$this->logFile]) && is_resource(self::$logFileHandles[$this->logFile] ?? false)) {
189  return;
190  }
191 
192  $this->‪createLogFile();
193  self::$logFileHandles[‪$this->logFile] = fopen($this->logFile, 'a');
194  if (!is_resource(self::$logFileHandles[$this->logFile])) {
195  throw new \RuntimeException('Could not open log file "' . $this->logFile . '"', 1321804422);
196  }
197  }
198 
202  protected function ‪closeLogFile()
203  {
204  if (!empty(self::$logFileHandles[$this->logFile]) && is_resource(self::$logFileHandles[$this->logFile])) {
205  fclose(self::$logFileHandles[$this->logFile]);
206  unset(self::$logFileHandles[$this->logFile]);
207  }
208  }
209 
214  protected function ‪createLogFile()
215  {
216  if (file_exists($this->logFile)) {
217  return;
218  }
219 
220  // skip mkdir if logFile refers to any scheme but vfs://, file:// or empty
221  $scheme = parse_url($this->logFile, PHP_URL_SCHEME);
222  if ($scheme === null || $scheme === 'file' || $scheme === 'vfs' || GeneralUtility::isAbsPath($this->logFile)) {
223  // remove file:/ before creating the directory
224  $logFileDirectory = ‪PathUtility::dirname((string)preg_replace('#^file:/#', '', $this->logFile));
225  if (!@is_dir($logFileDirectory)) {
226  ‪GeneralUtility::mkdir_deep($logFileDirectory);
227  // create .htaccess file if log file is within the site path
228  if (‪PathUtility::getCommonPrefix([‪Environment::getPublicPath() . '/', $logFileDirectory]) === (‪Environment::getPublicPath() . '/')) {
229  // only create .htaccess, if we created the directory on our own
230  $this->‪createHtaccessFile($logFileDirectory . '/.htaccess');
231  }
232  }
233  }
234  // create the log file
235  ‪GeneralUtility::writeFile($this->logFile, '');
236  }
237 
243  protected function ‪createHtaccessFile($htaccessFile)
244  {
245  // write .htaccess file to protect the log file
246  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['generateApacheHtaccess']) && !file_exists($htaccessFile)) {
247  $htaccessContent = '
248 # Apache < 2.3
249 <IfModule !mod_authz_core.c>
250  Order allow,deny
251  Deny from all
252  Satisfy All
253 </IfModule>
254 
255 # Apache ≥ 2.3
256 <IfModule mod_authz_core.c>
257  Require all denied
258 </IfModule>
259  ';
260  ‪GeneralUtility::writeFile($htaccessFile, $htaccessContent);
261  }
262  }
263 
271  protected function ‪getDefaultLogFileName()
272  {
273  $namePart = substr(GeneralUtility::hmac($this->defaultLogFileTemplate, 'defaultLogFile'), 0, 10);
274  if ($this->logFileInfix !== '') {
275  $namePart = $this->logFileInfix . '_' . $namePart;
276  }
277  return ‪Environment::getVarPath() . sprintf($this->defaultLogFileTemplate, $namePart);
278  }
279 }
‪TYPO3\CMS\Core\Log\Writer\FileWriter\setLogFileInfix
‪setLogFileInfix(string $infix)
Definition: FileWriter.php:91
‪TYPO3\CMS\Core\Log\Exception
Definition: Exception.php:22
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileInfix
‪string $logFileInfix
Definition: FileWriter.php:37
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:24
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:180
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static string dirname($path)
Definition: PathUtility.php:186
‪TYPO3\CMS\Core\Log\Writer\FileWriter\__destruct
‪__destruct()
Definition: FileWriter.php:83
‪TYPO3\CMS\Core\Log\Exception\InvalidLogWriterConfigurationException
Definition: InvalidLogWriterConfigurationException.php:24
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$defaultLogFileTemplate
‪string $defaultLogFileTemplate
Definition: FileWriter.php:43
‪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:2022
‪TYPO3\CMS\Core\Log\LogRecord
Definition: LogRecord.php:22
‪TYPO3\CMS\Core\Log\Writer\WriterInterface
Definition: WriterInterface.php:24
‪TYPO3\CMS\Core\Log\Writer\FileWriter\setLogFile
‪WriterInterface setLogFile($relativeLogFile)
Definition: FileWriter.php:103
‪TYPO3\CMS\Core\Utility\PathUtility\getCommonPrefix
‪static string null getCommonPrefix(array $paths)
Definition: PathUtility.php:110
‪TYPO3\CMS\Core\Log\LogRecord\getMessage
‪string getMessage()
Definition: LogRecord.php:222
‪TYPO3\CMS\Core\Log\Writer\FileWriter\getLogFile
‪string getLogFile()
Definition: FileWriter.php:127
‪TYPO3\CMS\Core\Log\LogRecord\getComponent
‪string getComponent()
Definition: LogRecord.php:118
‪TYPO3\CMS\Core\Log\Writer\FileWriter\createLogFile
‪createLogFile()
Definition: FileWriter.php:209
‪TYPO3\CMS\Core\Log\Writer\FileWriter\getDefaultLogFileName
‪string getDefaultLogFileName()
Definition: FileWriter.php:266
‪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:33
‪TYPO3\CMS\Core\Utility\PathUtility\isAbsolutePath
‪static bool isAbsolutePath($path)
Definition: PathUtility.php:223
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileHandles
‪static array $logFileHandles
Definition: FileWriter.php:53
‪TYPO3\CMS\Core\Log\Writer\AbstractWriter
Definition: AbstractWriter.php:25
‪TYPO3\CMS\Core\Log\Writer\FileWriter\openLogFile
‪openLogFile()
Definition: FileWriter.php:176
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:40
‪TYPO3\CMS\Core\Log\LogRecord\getCreated
‪float getCreated()
Definition: LogRecord.php:140
‪TYPO3\CMS\Core\Log\Writer\FileWriter\createHtaccessFile
‪createHtaccessFile($htaccessFile)
Definition: FileWriter.php:238
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileHandlesCount
‪static array $logFileHandlesCount
Definition: FileWriter.php:64
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFile
‪static bool writeFile($file, $content, $changePermissions=false)
Definition: GeneralUtility.php:1836
‪TYPO3\CMS\Core\Log\Writer\FileWriter\__construct
‪__construct(array $options=[])
Definition: FileWriter.php:71
‪TYPO3\CMS\Core\Log\LogRecord\getRequestId
‪string getRequestId()
Definition: LogRecord.php:244
‪TYPO3\CMS\Core\Log\Writer\FileWriter\closeLogFile
‪closeLogFile()
Definition: FileWriter.php:197
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static string getVarPath()
Definition: Environment.php:192
‪TYPO3\CMS\Core\Log\Writer\FileWriter\writeLog
‪WriterInterface writeLog(LogRecord $record)
Definition: FileWriter.php:139