1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @link https://www.yiiframework.com/ |
5
|
|
|
* @copyright Copyright (c) 2008 Yii Software LLC |
6
|
|
|
* @license https://www.yiiframework.com/license/ |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace yii\log; |
10
|
|
|
|
11
|
|
|
use Yii; |
12
|
|
|
use yii\base\InvalidConfigException; |
13
|
|
|
use yii\helpers\FileHelper; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* FileTarget records log messages in a file. |
17
|
|
|
* |
18
|
|
|
* The log file is specified via [[logFile]]. If the size of the log file exceeds |
19
|
|
|
* [[maxFileSize]] (in kilo-bytes), a rotation will be performed, which renames |
20
|
|
|
* the current log file by suffixing the file name with '.1'. All existing log |
21
|
|
|
* files are moved backwards by one place, i.e., '.2' to '.3', '.1' to '.2', and so on. |
22
|
|
|
* The property [[maxLogFiles]] specifies how many history files to keep. |
23
|
|
|
* |
24
|
|
|
* @author Qiang Xue <[email protected]> |
25
|
|
|
* @since 2.0 |
26
|
|
|
*/ |
27
|
|
|
class FileTarget extends Target |
28
|
|
|
{ |
29
|
|
|
/** |
30
|
|
|
* @var string|null log file path or [path alias](guide:concept-aliases). If not set, it will use the "@runtime/logs/app.log" file. |
31
|
|
|
* The directory containing the log files will be automatically created if not existing. |
32
|
|
|
*/ |
33
|
|
|
public $logFile; |
34
|
|
|
/** |
35
|
|
|
* @var bool whether log files should be rotated when they reach a certain [[maxFileSize|maximum size]]. |
36
|
|
|
* Log rotation is enabled by default. This property allows you to disable it, when you have configured |
37
|
|
|
* an external tools for log rotation on your server. |
38
|
|
|
* @since 2.0.3 |
39
|
|
|
*/ |
40
|
|
|
public $enableRotation = true; |
41
|
|
|
/** |
42
|
|
|
* @var int maximum log file size, in kilo-bytes. Defaults to 10240, meaning 10MB. |
43
|
|
|
*/ |
44
|
|
|
public $maxFileSize = 10240; // in KB |
45
|
|
|
/** |
46
|
|
|
* @var int number of log files used for rotation. Defaults to 5. |
47
|
|
|
*/ |
48
|
|
|
public $maxLogFiles = 5; |
49
|
|
|
/** |
50
|
|
|
* @var int|null the permission to be set for newly created log files. |
51
|
|
|
* This value will be used by PHP chmod() function. No umask will be applied. |
52
|
|
|
* If not set, the permission will be determined by the current environment. |
53
|
|
|
*/ |
54
|
|
|
public $fileMode; |
55
|
|
|
/** |
56
|
|
|
* @var int the permission to be set for newly created directories. |
57
|
|
|
* This value will be used by PHP chmod() function. No umask will be applied. |
58
|
|
|
* Defaults to 0775, meaning the directory is read-writable by owner and group, |
59
|
|
|
* but read-only for other users. |
60
|
|
|
*/ |
61
|
|
|
public $dirMode = 0775; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Initializes the route. |
65
|
|
|
* This method is invoked after the route is created by the route manager. |
66
|
|
|
*/ |
67
|
3 |
|
public function init() |
68
|
|
|
{ |
69
|
3 |
|
parent::init(); |
70
|
3 |
|
if ($this->logFile === null) { |
71
|
1 |
|
$this->logFile = Yii::$app->getRuntimePath() . '/logs/app.log'; |
72
|
|
|
} else { |
73
|
2 |
|
$this->logFile = Yii::getAlias($this->logFile); |
|
|
|
|
74
|
|
|
} |
75
|
3 |
|
if ($this->maxLogFiles < 1) { |
76
|
|
|
$this->maxLogFiles = 1; |
77
|
|
|
} |
78
|
3 |
|
if ($this->maxFileSize < 1) { |
79
|
|
|
$this->maxFileSize = 1; |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Writes log messages to a file. |
85
|
|
|
* Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported. |
86
|
|
|
* @throws InvalidConfigException if unable to open the log file for writing |
87
|
|
|
* @throws LogRuntimeException if unable to write complete log to file |
88
|
|
|
*/ |
89
|
2 |
|
public function export() |
90
|
|
|
{ |
91
|
2 |
|
$text = implode("\n", array_map([$this, 'formatMessage'], $this->messages)) . "\n"; |
92
|
|
|
|
93
|
2 |
|
if (trim($text) === '') { |
94
|
1 |
|
return; // No messages to export, so we exit the function early |
95
|
|
|
} |
96
|
|
|
|
97
|
2 |
|
if (strpos($this->logFile, '://') === false || strncmp($this->logFile, 'file://', 7) === 0) { |
|
|
|
|
98
|
2 |
|
$logPath = dirname($this->logFile); |
|
|
|
|
99
|
2 |
|
FileHelper::createDirectory($logPath, $this->dirMode, true); |
100
|
|
|
} |
101
|
|
|
|
102
|
2 |
|
if (($fp = @fopen($this->logFile, 'a')) === false) { |
|
|
|
|
103
|
|
|
throw new InvalidConfigException("Unable to append to log file: {$this->logFile}"); |
104
|
|
|
} |
105
|
2 |
|
@flock($fp, LOCK_EX); |
|
|
|
|
106
|
2 |
|
if ($this->enableRotation) { |
107
|
|
|
// clear stat cache to ensure getting the real current file size and not a cached one |
108
|
|
|
// this may result in rotating twice when cached file size is used on subsequent calls |
109
|
2 |
|
clearstatcache(); |
110
|
|
|
} |
111
|
2 |
|
if ($this->enableRotation && @filesize($this->logFile) > $this->maxFileSize * 1024) { |
|
|
|
|
112
|
1 |
|
$this->rotateFiles(); |
113
|
|
|
} |
114
|
2 |
|
$writeResult = @fwrite($fp, $text); |
115
|
2 |
|
if ($writeResult === false) { |
116
|
|
|
$message = "Unable to export log through file ($this->logFile)!"; |
117
|
|
|
if ($error = error_get_last()) { |
118
|
|
|
$message .= ": {$error['message']}"; |
119
|
|
|
} |
120
|
|
|
throw new LogRuntimeException($message); |
121
|
|
|
} |
122
|
2 |
|
$textSize = strlen($text); |
123
|
2 |
|
if ($writeResult < $textSize) { |
124
|
|
|
throw new LogRuntimeException("Unable to export whole log through file ({$this->logFile})! Wrote $writeResult out of $textSize bytes."); |
125
|
|
|
} |
126
|
2 |
|
@fflush($fp); |
|
|
|
|
127
|
2 |
|
@flock($fp, LOCK_UN); |
128
|
2 |
|
@fclose($fp); |
|
|
|
|
129
|
|
|
|
130
|
2 |
|
if ($this->fileMode !== null) { |
131
|
|
|
@chmod($this->logFile, $this->fileMode); |
|
|
|
|
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Rotates log files. |
137
|
|
|
*/ |
138
|
1 |
|
protected function rotateFiles() |
139
|
|
|
{ |
140
|
1 |
|
$file = $this->logFile; |
141
|
1 |
|
for ($i = $this->maxLogFiles; $i >= 0; --$i) { |
142
|
|
|
// $i == 0 is the original log file |
143
|
1 |
|
$rotateFile = $file . ($i === 0 ? '' : '.' . $i); |
144
|
1 |
|
if (is_file($rotateFile)) { |
145
|
|
|
// suppress errors because it's possible multiple processes enter into this section |
146
|
1 |
|
if ($i === $this->maxLogFiles) { |
147
|
1 |
|
@unlink($rotateFile); |
|
|
|
|
148
|
1 |
|
continue; |
149
|
|
|
} |
150
|
1 |
|
$newFile = $this->logFile . '.' . ($i + 1); |
151
|
1 |
|
$this->rotateByCopy($rotateFile, $newFile); |
152
|
1 |
|
if ($i === 0) { |
153
|
1 |
|
$this->clearLogFile($rotateFile); |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/*** |
160
|
|
|
* Clear log file without closing any other process open handles |
161
|
|
|
* @param string $rotateFile |
162
|
|
|
*/ |
163
|
1 |
|
private function clearLogFile($rotateFile) |
164
|
|
|
{ |
165
|
1 |
|
if ($filePointer = @fopen($rotateFile, 'a')) { |
166
|
1 |
|
@ftruncate($filePointer, 0); |
|
|
|
|
167
|
1 |
|
@fclose($filePointer); |
|
|
|
|
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/*** |
172
|
|
|
* Copy rotated file into new file |
173
|
|
|
* @param string $rotateFile |
174
|
|
|
* @param string $newFile |
175
|
|
|
*/ |
176
|
1 |
|
private function rotateByCopy($rotateFile, $newFile) |
177
|
|
|
{ |
178
|
1 |
|
@copy($rotateFile, $newFile); |
|
|
|
|
179
|
1 |
|
if ($this->fileMode !== null) { |
180
|
|
|
@chmod($newFile, $this->fileMode); |
|
|
|
|
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.