FileLoggerBase::packLogFile()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 13
rs 9.9332
1
<?php
2
3
4
namespace suda\framework\debug\log\logger;
5
6
use Psr\Log\AbstractLogger;
7
use RecursiveDirectoryIterator;
8
use RecursiveIteratorIterator;
9
use suda\framework\debug\ConfigInterface;
10
use suda\framework\debug\ConfigTrait;
11
use suda\framework\debug\log\logger\exception\FileLoggerException;
12
use ZipArchive;
13
14
abstract class FileLoggerBase extends AbstractLogger implements ConfigInterface
15
{
16
    use ConfigTrait;
17
18
    /**
19
     * 文件
20
     *
21
     * @var resource
22
     */
23
    protected $temp;
24
    /**
25
     * 临时文件名
26
     *
27
     * @var string
28
     */
29
    protected $tempName;
30
    /**
31
     * 移除文件
32
     *
33
     * @var array
34
     */
35
    protected $removeFiles = [];
36
    /**
37
     * 最后的日志
38
     *
39
     * @var string
40
     */
41
    protected $latest;
42
43
    /**
44
     * @return array
45
     */
46
    public function getDefaultConfig(): array
47
    {
48
        return [
49
            'save-path' => './logs',
50
            'save-zip-path' => './logs/zip',
51
            'save-dump-path' => './logs/dump',
52
            'max-file-size' => 2097152,
53
            'file-name' => 'latest.log',
54
            'log-level' => 'debug',
55
            'log-format' => '[%level%] %message%',
56
        ];
57
    }
58
59
    /**
60
     * @return resource
61
     * @throws FileLoggerException
62
     */
63
    public function getAvailableWrite()
64
    {
65
        if (is_resource($this->temp)) {
66
            return $this->temp;
67
        }
68
        $this->prepareWrite();
69
        return $this->temp;
70
    }
71
72
    /**
73
     * 检查日志文件大小
74
     *
75
     * @return boolean
76
     */
77
    protected function checkSize(): bool
78
    {
79
        $logFile = $this->latest;
80
        if ($this->getConfig('max-file-size') == 0) {
81
            return false;
82
        }
83
        if (file_exists($logFile)) {
84
            if (filesize($logFile) > $this->getConfig('max-file-size')) {
85
                return true;
86
            }
87
        }
88
        return false;
89
    }
90
91
    /**
92
     * 删除已经压缩的文件
93
     */
94
    protected function removePackFiles()
95
    {
96
        foreach ($this->removeFiles as $file) {
97
            if (is_file($file) && file_exists($file)) {
98
                unlink($file);
99
            }
100
        }
101
        $this->removeFiles = [];
102
    }
103
104
    /**
105
     * @param string $from
106
     * @param string $to
107
     */
108
    protected function safeMoveKeep(string $from, string $to)
109
    {
110
        $fromFile = fopen($from, 'r');
111
        $toFile = fopen($to, 'w+');
112
        if ($fromFile !== false && $toFile !== false) {
113
            flock($toFile, LOCK_EX);
114
            // 复制内容
115
            stream_copy_to_stream($fromFile, $toFile);
116
            flock($toFile, LOCK_UN);
117
            fclose($toFile);
118
            fclose($fromFile);
119
        }
120
        // 清空内容
121
        $this->clearContent($from);
122
    }
123
124
    /**
125
     * @throws FileLoggerException
126
     */
127
    protected function prepareWrite()
128
    {
129
        $unique = substr(md5(uniqid()), 0, 8);
130
        $save = $this->getConfig('save-path');
131
        $this->tempName = $save . '/' . date('YmdHis') . '.' . $unique . '.log';
132
        $temp = fopen($this->tempName, 'w+');
133
        if ($temp !== false) {
134
            $this->temp = $temp;
135
        } else {
136
            throw new FileLoggerException(__METHOD__ . ':' . sprintf('cannot create log file'));
137
        }
138
        $this->latest = $save . '/' . $this->getConfig('file-name');
139
    }
140
141
    /**
142
     * 获取压缩
143
     *
144
     * @param string $path
145
     * @return ZipArchive|null
146
     */
147
    protected function getZipArchive(string $path)
148
    {
149
        if (class_exists('ZipArchive')) {
150
            $zip = new ZipArchive;
151
            $res = $zip->open($path, ZipArchive::CREATE);
152
            if ($res === true) {
153
                return $zip;
154
            }
155
        }
156
        return null;
157
    }
158
159
    /**
160
     * @param ZipArchive $zip
161
     * @param string $logFile
162
     */
163
    protected function zipFile(ZipArchive $zip, string $logFile)
164
    {
165
        $add = $zip->addFile($logFile, date('Y-m-d') . '-' . $zip->numFiles . '.log');
166
        if (is_dir($this->getConfig('save-dump-path'))) {
167
            $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(
168
                $this->getConfig('save-dump-path'),
169
                RecursiveDirectoryIterator::SKIP_DOTS
170
            ));
171
            foreach ($it as $dumpLog) {
172
                if ($zip->addFile($dumpLog, 'dump/' . basename($dumpLog))) {
173
                    array_push($this->removeFiles, $dumpLog);
174
                }
175
            }
176
        }
177
        $zip->close();
178
        if ($add) {
179
            $this->clearContent($logFile);
180
        }
181
    }
182
183
    /**
184
     * @param string $logFile
185
     */
186
    protected function moveFile(string $logFile)
187
    {
188
        if (is_file($logFile) && file_exists($logFile)) {
189
            $this->safeMoveKeep(
190
                $logFile,
191
                $this->getConfig('save-path')
192
                . '/' . date('Y-m-d')
193
                . '-' . substr(md5(uniqid()), 0, 8) . '.log'
194
            );
195
        }
196
    }
197
198
    /**
199
     * 清空内容
200
     * @param string $path
201
     * @return bool
202
     */
203
    protected function clearContent(string $path)
204
    {
205
        $file = fopen($path, 'w');
206
        if ($file !== false) {
207
            fclose($file);
208
            return true;
209
        }
210
        return false;
211
    }
212
213
    /**
214
     * 打包文件
215
     */
216
    protected function packLogFile()
217
    {
218
        $logFile = $this->latest;
219
        $path = preg_replace(
220
            '/[\\\\]+/',
221
            '/',
222
            $this->getConfig('save-zip-path') . '/' . date('Y-m-d') . '.zip'
223
        );
224
        $zip = $this->getZipArchive($path);
225
        if ($zip !== null) {
226
            $this->zipFile($zip, $logFile);
227
        } else {
228
            $this->moveFile($logFile);
229
        }
230
    }
231
232
    /**
233
     * 将临时文件写入最后日志
234
     */
235
    protected function rollLatest()
236
    {
237
        if (isset($this->latest)) {
238
            $latest = fopen($this->latest, 'a+');
239
            if ($latest !== false && flock($latest, LOCK_EX)) {
240
                rewind($this->temp);
241
                stream_copy_to_stream($this->temp, $latest);
242
                flock($latest, LOCK_UN);
243
                fclose($latest);
244
            }
245
            fclose($this->temp);
246
            if (file_exists($this->tempName)) {
247
                unlink($this->tempName);
248
            }
249
            $this->temp = null;
250
            $this->tempName = null;
251
        }
252
    }
253
}
254