[BUGFIX] Handle exceptions in Logging API
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Log / Writer / FileWriter.php
1 <?php
2 namespace TYPO3\CMS\Core\Log\Writer;
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 TYPO3\CMS\Core\Log\Exception\InvalidLogWriterConfigurationException;
18 use TYPO3\CMS\Core\Log\LogRecord;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * Log writer that writes the log records into a file.
23 *
24 * @author Steffen Gebert <steffen.gebert@typo3.org>
25 * @author Steffen Müller <typo3@t3node.com>
26 * @author Ingo Renner <ingo@typo3.org>
27 */
28 class FileWriter extends AbstractWriter {
29
30 /**
31 * Log file path, relative to PATH_site
32 *
33 * @var string
34 */
35 protected $logFile = '';
36
37 /**
38 * Default log file path
39 *
40 * @var string
41 */
42 protected $defaultLogFile = 'typo3temp/logs/typo3.log';
43
44 /**
45 * Log file handle storage
46 *
47 * To avoid concurrent file handles on a the same file when using several FileWriter instances,
48 * we share the file handles in a static class variable
49 *
50 * @static
51 * @var array
52 */
53 static protected $logFileHandles = array();
54
55 /**
56 * Constructor, opens the log file handle
57 *
58 * @param array $options
59 * @return FileWriter
60 */
61 public function __construct(array $options = array()) {
62 // the parent constructor reads $options and sets them
63 parent::__construct($options);
64 if (empty($options['logFile'])) {
65 $this->setLogFile($this->defaultLogFile);
66 }
67 }
68
69 /**
70 * Destructor, closes the log file handle
71 */
72 public function __destruct() {
73 $this->closeLogFile();
74 }
75
76 /**
77 * Sets the path to the log file.
78 *
79 * @param string $logFile path to the log file, relative to PATH_site
80 * @return WriterInterface
81 * @throws InvalidLogWriterConfigurationException
82 */
83 public function setLogFile($logFile) {
84
85 // Skip handling if logFile is a stream resource. This is used by unit tests with vfs:// directories
86 if (FALSE === strpos($logFile, '://')) {
87 if (!GeneralUtility::isAllowedAbsPath((PATH_site . $logFile))) {
88 throw new InvalidLogWriterConfigurationException('Log file path "' . $logFile . '" is not valid!', 1326411176);
89 }
90 $logFile = GeneralUtility::getFileAbsFileName($logFile);
91 }
92 $this->logFile = $logFile;
93 $this->openLogFile();
94
95 return $this;
96 }
97
98 /**
99 * Gets the path to the log file.
100 *
101 * @return string Path to the log file.
102 */
103 public function getLogFile() {
104 return $this->logFile;
105 }
106
107 /**
108 * Writes the log record
109 *
110 * @param LogRecord $record Log record
111 * @return WriterInterface $this
112 * @throws \RuntimeException
113 */
114 public function writeLog(LogRecord $record) {
115 if (FALSE === fwrite(self::$logFileHandles[$this->logFile], $record . LF)) {
116 throw new \RuntimeException('Could not write log record to log file', 1345036335);
117 }
118
119 return $this;
120 }
121
122 /**
123 * Opens the log file handle
124 *
125 * @return void
126 * @throws \RuntimeException if the log file can't be opened.
127 */
128 protected function openLogFile() {
129 if (is_resource(self::$logFileHandles[$this->logFile])) {
130 return;
131 }
132
133 $this->createLogFile();
134 self::$logFileHandles[$this->logFile] = fopen($this->logFile, 'a');
135 if (!is_resource(self::$logFileHandles[$this->logFile])) {
136 throw new \RuntimeException('Could not open log file "' . $this->logFile . '"', 1321804422);
137 }
138 }
139
140 /**
141 * Closes the log file handle.
142 *
143 * @return void
144 */
145 protected function closeLogFile() {
146 if (is_resource(self::$logFileHandles[$this->logFile])) {
147 fclose(self::$logFileHandles[$this->logFile]);
148 unset(self::$logFileHandles[$this->logFile]);
149 }
150 }
151
152 /**
153 * Creates the log file with correct permissions
154 * and parent directories, if needed
155 *
156 * @return void
157 */
158 protected function createLogFile() {
159 if (file_exists($this->logFile)) {
160 return;
161 }
162 $logFileDirectory = dirname($this->logFile);
163 if (!@is_dir($logFileDirectory)) {
164 GeneralUtility::mkdir_deep($logFileDirectory);
165 // only create .htaccess, if we created the directory on our own
166 $this->createHtaccessFile($logFileDirectory . '/.htaccess');
167 }
168 // create the log file
169 GeneralUtility::writeFile($this->logFile, '');
170 }
171
172 /**
173 * Creates .htaccess file inside a new directory to access protect it
174 *
175 * @param string $htaccessFile Path of .htaccess file
176 * @return void
177 */
178 protected function createHtaccessFile($htaccessFile) {
179 // write .htaccess file to protect the log file
180 if (!empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['generateApacheHtaccess']) && !file_exists($htaccessFile)) {
181 GeneralUtility::writeFile($htaccessFile, 'Deny From All');
182 }
183 }
184
185 }