-
Notifications
You must be signed in to change notification settings - Fork 5
/
LogTarget.php
196 lines (170 loc) · 5.16 KB
/
LogTarget.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
<?php
namespace pastuhov\logstock;
use Yii;
use yii\base\InvalidConfigException;
use yii\helpers\FileHelper;
use yii\log\Logger;
use yii\log\Target;
/**
* The logstock LogTarget is used to store logs
*/
class LogTarget extends Target
{
/**
* @var Module
*/
public $module;
public $tag;
/**
* {@inheritdoc}
*/
public $enabled = false;
/**
* @param \yii\debug\Module $module
* @param array $config
*/
public function __construct($module, $config = [])
{
parent::__construct($config);
$this->module = $module;
$this->tag = uniqid();
}
/**
* Exports log messages to a specific destination.
* Child classes must implement this method.
*/
public function export()
{
$path = $this->module->dataPath;
FileHelper::createDirectory($path, $this->module->dirMode);
$summary = $this->collectSummary();
$dataFile = "$path/{$this->tag}.log";
$data = [];
$data['log'] = $this->save();
$data['summary'] = $summary;
file_put_contents($dataFile, $this->convertArrayToText($data));
if ($this->module->fileMode !== null) {
@chmod($dataFile, $this->module->fileMode);
}
$indexFile = "$path/index.data";
$this->updateIndexFile($indexFile, $summary);
}
/**
*
*/
public function save()
{
$messages = $this->filterMessages(
$this->messages,
Logger::LEVEL_ERROR | Logger::LEVEL_INFO | Logger::LEVEL_WARNING | Logger::LEVEL_TRACE,
[],
[
'yii\db\Connection::open',
'yii\db\Connection::close',
]
);
foreach ($messages as &$message) {
// exceptions may not be serializable if in the call stack somewhere is a Closure
if ($message[0] instanceof \Throwable || $message[0] instanceof \Exception) {
$message[0] = (string)$message[0];
}
}
return ['messages' => $messages];
}
/**
* Updates index file with summary log data
*
* @param string $indexFile path to index file
* @param array $summary summary log data
* @throws \yii\base\InvalidConfigException
*/
private function updateIndexFile($indexFile, $summary)
{
touch($indexFile);
if (($fp = @fopen($indexFile, 'r+')) === false) {
throw new InvalidConfigException("Unable to open debug data index file: $indexFile");
}
@flock($fp, LOCK_EX);
$manifest = '';
while (($buffer = fgets($fp)) !== false) {
$manifest .= $buffer;
}
if (!feof($fp) || empty($manifest)) {
// error while reading index data, ignore and create new
$manifest = [];
} else {
$manifest = unserialize($manifest);
}
$manifest[$this->tag] = $summary;
$this->gc($manifest);
ftruncate($fp, 0);
rewind($fp);
fwrite($fp, serialize($manifest));
@flock($fp, LOCK_UN);
@fclose($fp);
if ($this->module->fileMode !== null) {
@chmod($indexFile, $this->module->fileMode);
}
}
/**
* Processes the given log messages.
* This method will filter the given messages with [[levels]] and [[categories]].
* And if requested, it will also export the filtering result to specific medium (e.g. email).
* @param array $messages log messages to be processed. See [[\yii\log\Logger::messages]] for the structure
* of each message.
* @param bool $final whether this method is called at the end of the current application
*/
public function collect($messages, $final)
{
$this->messages = array_merge($this->messages, $messages);
if ($final) {
$this->export();
}
}
protected function gc(&$manifest)
{
if (count($manifest) > $this->module->historySize + 10) {
$n = count($manifest) - $this->module->historySize;
foreach (array_keys($manifest) as $tag) {
$file = $this->module->dataPath . "/$tag.data";
@unlink($file);
unset($manifest[$tag]);
if (--$n <= 0) {
break;
}
}
}
}
/**
* Collects summary data of current request.
* @return array
*/
protected function collectSummary()
{
if (Yii::$app === null) {
return '';
}
$request = Yii::$app->getRequest();
$entry = '';
try {
$entry = $request->getAbsoluteUrl();
} catch (\Exception $e) {
$entry = 'console';
}
$summary = [
'tag' => $this->tag,
'entry' => $entry,
];
return $summary;
}
protected function convertArrayToText($array)
{
$text = 'Entry: ' . $array['summary']['entry'] . PHP_EOL;
foreach ($array['log']['messages'] as $key=>$message) {
if (is_string($message[0])) {
$text .= trim($message[0]) . PHP_EOL;
}
}
return $text;
}
}