Passed
Push — dev ( a28fc9...ad5e2a )
by 世昌
02:19
created

FileLoggerBase   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 234
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 95
c 1
b 0
f 0
dl 0
loc 234
rs 9.6
wmc 35

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefaultConfig() 0 10 1
A safeMoveKeep() 0 14 3
A clearContent() 0 8 2
A removePackFiles() 0 8 4
A packLogFile() 0 13 2
A checkSize() 0 9 3
A rollLatest() 0 16 5
A moveFile() 0 8 3
A getZipArchive() 0 10 3
A prepareWrite() 0 12 2
A zipFile() 0 17 5
A getAvailableWrite() 0 7 2
1
<?php
2
3
4
namespace suda\framework\debug\log\logger;
5
6
use RecursiveDirectoryIterator;
7
use RecursiveIteratorIterator;
8
use suda\framework\debug\ConfigInterface;
9
use suda\framework\debug\ConfigTrait;
10
use suda\framework\debug\log\AbstractLogger;
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 (file_exists($logFile)) {
81
            if (filesize($logFile) > $this->getConfig('max-file-size')) {
82
                return true;
83
            }
84
        }
85
        return false;
86
    }
87
88
    /**
89
     * 删除已经压缩的文件
90
     */
91
    protected function removePackFiles()
92
    {
93
        foreach ($this->removeFiles as $file) {
94
            if (is_file($file) && file_exists($file)) {
95
                unlink($file);
96
            }
97
        }
98
        $this->removeFiles = [];
99
    }
100
101
    /**
102
     * @param string $from
103
     * @param string $to
104
     */
105
    protected function safeMoveKeep(string $from, string $to)
106
    {
107
        $fromFile = fopen($from, 'r');
108
        $toFile = fopen($to, 'w+');
109
        if ($fromFile !== false && $toFile !== false) {
110
            flock($toFile, LOCK_EX);
111
            // 复制内容
112
            stream_copy_to_stream($fromFile, $toFile);
113
            flock($toFile, LOCK_UN);
114
            fclose($toFile);
115
            fclose($fromFile);
116
        }
117
        // 清空内容
118
        $this->clearContent($from);
119
    }
120
121
    /**
122
     * @throws FileLoggerException
123
     */
124
    protected function prepareWrite()
125
    {
126
        $unique = substr(md5(uniqid()), 0, 8);
127
        $save = $this->getConfig('save-path');
128
        $this->tempName = $save . '/' . date('YmdHis') . '.' . $unique . '.log';
129
        $temp = fopen($this->tempName, 'w+');
130
        if ($temp !== false) {
131
            $this->temp = $temp;
132
        } else {
133
            throw new FileLoggerException(__METHOD__ . ':' . sprintf('cannot create log file'));
134
        }
135
        $this->latest = $save . '/' . $this->getConfig('file-name');
136
    }
137
138
    /**
139
     * 获取压缩
140
     *
141
     * @param string $path
142
     * @return ZipArchive|null
143
     */
144
    protected function getZipArchive(string $path)
145
    {
146
        if (class_exists('ZipArchive')) {
147
            $zip = new ZipArchive;
148
            $res = $zip->open($path, ZipArchive::CREATE);
149
            if ($res === true) {
150
                return $zip;
151
            }
152
        }
153
        return null;
154
    }
155
156
    /**
157
     * @param ZipArchive $zip
158
     * @param string $logFile
159
     */
160
    protected function zipFile(ZipArchive $zip, string $logFile)
161
    {
162
        $add = $zip->addFile($logFile, date('Y-m-d') . '-' . $zip->numFiles . '.log');
163
        if (is_dir($this->getConfig('save-dump-path'))) {
164
            $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(
165
                $this->getConfig('save-dump-path'),
166
                RecursiveDirectoryIterator::SKIP_DOTS
167
            ));
168
            foreach ($it as $dumpLog) {
169
                if ($zip->addFile($dumpLog, 'dump/' . basename($dumpLog))) {
170
                    array_push($this->removeFiles, $dumpLog);
171
                }
172
            }
173
        }
174
        $zip->close();
175
        if ($add) {
176
            $this->clearContent($logFile);
177
        }
178
    }
179
180
    /**
181
     * @param string $logFile
182
     */
183
    protected function moveFile(string $logFile)
184
    {
185
        if (is_file($logFile) && file_exists($logFile)) {
186
            $this->safeMoveKeep(
187
                $logFile,
188
                $this->getConfig('save-path')
189
                . '/' . date('Y-m-d')
190
                . '-' . substr(md5(uniqid()), 0, 8) . '.log'
191
            );
192
        }
193
    }
194
195
    /**
196
     * 清空内容
197
     * @param string $path
198
     * @return bool
199
     */
200
    protected function clearContent(string $path)
201
    {
202
        $file = fopen($path, 'w');
203
        if ($file !== false) {
204
            fclose($file);
205
            return true;
206
        }
207
        return false;
208
    }
209
210
    /**
211
     * 打包文件
212
     */
213
    protected function packLogFile()
214
    {
215
        $logFile = $this->latest;
216
        $path = preg_replace(
217
            '/[\\\\]+/',
218
            '/',
219
            $this->getConfig('save-zip-path') . '/' . date('Y-m-d') . '.zip'
220
        );
221
        $zip = $this->getZipArchive($path);
222
        if ($zip !== null) {
223
            $this->zipFile($zip, $logFile);
224
        } else {
225
            $this->moveFile($logFile);
226
        }
227
    }
228
229
    /**
230
     * 将临时文件写入最后日志
231
     */
232
    protected function rollLatest()
233
    {
234
        if (isset($this->latest)) {
235
            $latest = fopen($this->latest, 'a+');
236
            if ($latest !== false && flock($latest, LOCK_EX)) {
237
                rewind($this->temp);
238
                stream_copy_to_stream($this->temp, $latest);
239
                flock($latest, LOCK_UN);
240
                if (file_exists($this->tempName)) {
241
                    unlink($this->tempName);
242
                }
243
                fclose($latest);
244
            }
245
            fclose($this->temp);
246
            $this->temp = null;
247
            $this->tempName = null;
248
        }
249
    }
250
}
251