‪TYPO3CMS  9.5
FileWriter.php
Go to the documentation of this file.
1 <?php
2 
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
24 
29 {
35  protected ‪$logFile = '';
36 
40  protected ‪$logFileInfix = '';
41 
47  protected ‪$defaultLogFileTemplate = '/log/typo3_%s.log';
48 
58  protected static ‪$logFileHandles = [];
59 
70  protected static ‪$logFileHandlesCount = [];
71 
77  public function ‪__construct(array $options = [])
78  {
79  // the parent constructor reads $options and sets them
80  parent::__construct($options);
81  if (empty($options['logFile'])) {
82  $this->‪setLogFile($this->‪getDefaultLogFileName());
83  }
84  }
85 
89  public function ‪__destruct()
90  {
91  self::$logFileHandlesCount[‪$this->logFile]--;
92  if (self::$logFileHandlesCount[$this->logFile] <= 0) {
93  $this->‪closeLogFile();
94  }
95  }
96 
97  public function ‪setLogFileInfix(string $infix)
98  {
99  $this->logFileInfix = $infix;
100  }
101 
109  public function ‪setLogFile($relativeLogFile)
110  {
111  ‪$logFile = $relativeLogFile;
112  // Skip handling if logFile is a stream resource. This is used by unit tests with vfs:// directories
113  if (false === strpos(‪$logFile, '://') && !‪PathUtility::isAbsolutePath(‪$logFile)) {
114  ‪$logFile = GeneralUtility::getFileAbsFileName(‪$logFile);
115  if (empty(‪$logFile)) {
116  throw new InvalidLogWriterConfigurationException(
117  'Log file path "' . $relativeLogFile . '" is not valid!',
118  1444374805
119  );
120  }
121  }
122  $this->logFile = ‪$logFile;
123  $this->‪openLogFile();
124 
125  return $this;
126  }
127 
133  public function ‪getLogFile()
134  {
135  return ‪$this->logFile;
136  }
137 
145  public function ‪writeLog(LogRecord $record)
146  {
147  $timestamp = date('r', (int)$record->getCreated());
148  $levelName = ‪LogLevel::getName($record->getLevel());
149  $data = '';
150  $recordData = $record->getData();
151  if (!empty($recordData)) {
152  // According to PSR3 the exception-key may hold an \Exception
153  // Since json_encode() does not encode an exception, we run the _toString() here
154  if (isset($recordData['exception']) && $recordData['exception'] instanceof \Exception) {
155  $recordData['exception'] = (string)$recordData['exception'];
156  }
157  $data = '- ' . json_encode($recordData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
158  }
159 
160  $message = sprintf(
161  '%s [%s] request="%s" component="%s": %s %s',
162  $timestamp,
163  $levelName,
164  $record->getRequestId(),
165  $record->getComponent(),
166  $record->getMessage(),
167  $data
168  );
169 
170  if (false === fwrite(self::$logFileHandles[$this->logFile], $message . LF)) {
171  throw new \RuntimeException('Could not write log record to log file', 1345036335);
172  }
173 
174  return $this;
175  }
176 
182  protected function ‪openLogFile()
183  {
184  if (isset(self::$logFileHandlesCount[$this->logFile])) {
185  self::$logFileHandlesCount[‪$this->logFile]++;
186  } else {
187  self::$logFileHandlesCount[‪$this->logFile] = 1;
188  }
189  if (isset(self::$logFileHandles[$this->logFile]) && is_resource(self::$logFileHandles[$this->logFile] ?? false)) {
190  return;
191  }
192 
193  $this->‪createLogFile();
194  self::$logFileHandles[‪$this->logFile] = fopen($this->logFile, 'a');
195  if (!is_resource(self::$logFileHandles[$this->logFile])) {
196  throw new \RuntimeException('Could not open log file "' . $this->logFile . '"', 1321804422);
197  }
198  }
199 
203  protected function ‪closeLogFile()
204  {
205  if (!empty(self::$logFileHandles[$this->logFile]) && is_resource(self::$logFileHandles[$this->logFile])) {
206  fclose(self::$logFileHandles[$this->logFile]);
207  unset(self::$logFileHandles[$this->logFile]);
208  }
209  }
210 
215  protected function ‪createLogFile()
216  {
217  if (file_exists($this->logFile)) {
218  return;
219  }
220 
221  // skip mkdir if logFile refers to any scheme but vfs://, file:// or empty
222  $scheme = parse_url($this->logFile, PHP_URL_SCHEME);
223  if ($scheme === null || $scheme === 'file' || $scheme === 'vfs' || GeneralUtility::isAbsPath($this->logFile)) {
224  // remove file:/ before creating the directory
225  $logFileDirectory = ‪PathUtility::dirname(preg_replace('#^file:/#', '', $this->logFile));
226  if (!@is_dir($logFileDirectory)) {
227  GeneralUtility::mkdir_deep($logFileDirectory);
228  // create .htaccess file if log file is within the site path
229  if (‪PathUtility::getCommonPrefix([‪Environment::getPublicPath() . '/', $logFileDirectory]) === (‪Environment::getPublicPath() . '/')) {
230  // only create .htaccess, if we created the directory on our own
231  $this->‪createHtaccessFile($logFileDirectory . '/.htaccess');
232  }
233  }
234  }
235  // create the log file
236  GeneralUtility::writeFile($this->logFile, '');
237  }
238 
244  protected function ‪createHtaccessFile($htaccessFile)
245  {
246  // write .htaccess file to protect the log file
247  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['generateApacheHtaccess']) && !file_exists($htaccessFile)) {
248  $htaccessContent = '
249 # Apache < 2.3
250 <IfModule !mod_authz_core.c>
251  Order allow,deny
252  Deny from all
253  Satisfy All
254 </IfModule>
255 
256 # Apache ≥ 2.3
257 <IfModule mod_authz_core.c>
258  Require all denied
259 </IfModule>
260  ';
261  GeneralUtility::writeFile($htaccessFile, $htaccessContent);
262  }
263  }
264 
272  protected function ‪getDefaultLogFileName()
273  {
274  $namePart = substr(GeneralUtility::hmac($this->defaultLogFileTemplate, 'defaultLogFile'), 0, 10);
275  if ($this->logFileInfix !== '') {
276  $namePart = $this->logFileInfix . '_' . $namePart;
277  }
278  return ‪Environment::getVarPath() . sprintf($this->defaultLogFileTemplate, $namePart);
279  }
280 }
‪TYPO3\CMS\Core\Log\Writer\FileWriter\setLogFileInfix
‪setLogFileInfix(string $infix)
Definition: FileWriter.php:92
‪TYPO3\CMS\Core\Log\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileInfix
‪string $logFileInfix
Definition: FileWriter.php:38
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:23
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:153
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static string dirname($path)
Definition: PathUtility.php:185
‪TYPO3\CMS\Core\Log\Writer\FileWriter\__destruct
‪__destruct()
Definition: FileWriter.php:84
‪TYPO3\CMS\Core\Log\Exception\InvalidLogWriterConfigurationException
Definition: InvalidLogWriterConfigurationException.php:21
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$defaultLogFileTemplate
‪string $defaultLogFileTemplate
Definition: FileWriter.php:44
‪TYPO3\CMS\Core\Log\LogRecord\getData
‪array getData()
Definition: LogRecord.php:186
‪TYPO3\CMS\Core\Log\LogRecord
Definition: LogRecord.php:21
‪TYPO3\CMS\Core\Log\Writer\WriterInterface
Definition: WriterInterface.php:21
‪TYPO3\CMS\Core\Log\Writer\FileWriter\setLogFile
‪WriterInterface setLogFile($relativeLogFile)
Definition: FileWriter.php:104
‪TYPO3\CMS\Core\Utility\PathUtility\getCommonPrefix
‪static string null getCommonPrefix(array $paths)
Definition: PathUtility.php:109
‪TYPO3\CMS\Core\Log\LogRecord\getMessage
‪string getMessage()
Definition: LogRecord.php:221
‪TYPO3\CMS\Core\Log\Writer\FileWriter\getLogFile
‪string getLogFile()
Definition: FileWriter.php:128
‪TYPO3\CMS\Core\Log\LogRecord\getComponent
‪string getComponent()
Definition: LogRecord.php:117
‪TYPO3\CMS\Core\Log\Writer\FileWriter\createLogFile
‪createLogFile()
Definition: FileWriter.php:210
‪TYPO3\CMS\Core\Log\Writer\FileWriter\getDefaultLogFileName
‪string getDefaultLogFileName()
Definition: FileWriter.php:267
‪TYPO3\CMS\Core\Log\Writer\FileWriter
Definition: FileWriter.php:29
‪TYPO3\CMS\Core\Log\Writer
Definition: AbstractWriter.php:2
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFile
‪string $logFile
Definition: FileWriter.php:34
‪TYPO3\CMS\Core\Utility\PathUtility\isAbsolutePath
‪static bool isAbsolutePath($path)
Definition: PathUtility.php:222
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileHandles
‪static array $logFileHandles
Definition: FileWriter.php:54
‪TYPO3\CMS\Core\Log\Writer\AbstractWriter
Definition: AbstractWriter.php:24
‪TYPO3\CMS\Core\Log\Writer\FileWriter\openLogFile
‪openLogFile()
Definition: FileWriter.php:177
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:39
‪TYPO3\CMS\Core\Log\LogLevel\getName
‪static string getName($level)
Definition: LogLevel.php:117
‪TYPO3\CMS\Core\Log\LogRecord\getCreated
‪float getCreated()
Definition: LogRecord.php:139
‪TYPO3\CMS\Core\Log\Writer\FileWriter\createHtaccessFile
‪createHtaccessFile($htaccessFile)
Definition: FileWriter.php:239
‪TYPO3\CMS\Core\Log\Writer\FileWriter\$logFileHandlesCount
‪static array $logFileHandlesCount
Definition: FileWriter.php:65
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Log\LogRecord\getLevel
‪int getLevel()
Definition: LogRecord.php:164
‪TYPO3\CMS\Core\Log\Writer\FileWriter\__construct
‪__construct(array $options=[])
Definition: FileWriter.php:72
‪TYPO3\CMS\Core\Log\LogRecord\getRequestId
‪string getRequestId()
Definition: LogRecord.php:243
‪TYPO3\CMS\Core\Log\LogLevel
Definition: LogLevel.php:21
‪TYPO3\CMS\Core\Log\Writer\FileWriter\closeLogFile
‪closeLogFile()
Definition: FileWriter.php:198
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static string getVarPath()
Definition: Environment.php:165
‪TYPO3\CMS\Core\Log\Writer\FileWriter\writeLog
‪WriterInterface writeLog(LogRecord $record)
Definition: FileWriter.php:140