Passed
Push — master ( c547da...5d2787 )
by 世昌
02:10 queued 15s
created

FileLogger::interpolate()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 5
nop 2
dl 0
loc 12
rs 9.6111
c 0
b 0
f 0
1
<?php
2
namespace suda\component\debug\log\logger;
3
4
use ZipArchive;
5
use suda\component\debug\ConfigTrait;
6
use suda\component\debug\log\LogLevel;
7
use suda\component\debug\ConfigInterface;
8
use suda\component\debug\log\AbstractLogger;
9
use suda\component\debug\log\logger\exception\FileLoggerException;
10
11
class FileLogger extends AbstractLogger implements ConfigInterface
12
{
13
    use ConfigTrait;
14
15
    /**
16
     * 文件
17
     *
18
     * @var resource
19
     */
20
    protected $temp;
21
22
    /**
23
     * 临时文件名
24
     *
25
     * @var string
26
     */
27
    protected $tempname;
28
29
    /**
30
     * 移除文件
31
     *
32
     * @var array
33
     */
34
    protected $removeFiles = [];
35
36
    /**
37
     * 最后的日志
38
     *
39
     * @var string
40
     */
41
    protected $latest;
42
43
    /**
44
     * 构建文件日志
45
     *
46
     * @throws FileLoggerException
47
     * @param array $config
48
     */
49
    public function __construct(array $config=[])
50
    {
51
        $this->applyConfig($config);
52
        $temp = tmpfile();
53
        if ($temp === false) {
54
            $this->tempname = $this->getConfig('save-path').'/log-'. microtime(true).'.tmp';
55
            $temp = fopen($this->tempname, 'w+');
56
        }
57
        if ($temp !== false) {
58
            $this->temp = $temp;
59
        } else {
60
            throw new FileLoggerException(__CLASS__.':'.sprintf('cannot create log file'));
61
        }
62
        $this->latest = $this->getConfig('save-path').'/'.$this->getConfig('file-name');
63
        \register_shutdown_function([$this,'save']);
64
    }
65
66
    public function getDefaultConfig():array
67
    {
68
        return [
69
            'save-path' => './logs',
70
            'save-zip-path' => './logs/zip',
71
            'save-pack-path' => './logs/dump',
72
            'max-file-size' => 2097152,
73
            'file-name' => 'latest.log',
74
            'log-level' => 'debug',
75
            'log-format' => '[%level%] %message%',
76
        ];
77
    }
78
    
79
    protected function packLogFile()
80
    {
81
        $logFile= $this->latest;
82
        $path=preg_replace('/[\\\\]+/', '/', $this->getConfig('save-zip-path') .'/'.date('Y-m-d').'.zip');
83
        $zip = new ZipArchive;
84
        $res = $zip->open($path, ZipArchive::CREATE);
85
        if ($res === true) {
86
            if ($zip->addFile($logFile, date('Y-m-d'). '-'. $zip->numFiles .'.log')) {
87
                array_push($this->removeFiles, $logFile);
88
            }
89
            if (\is_dir($this->getConfig('save-pack-path'))) {
90
                $it = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getConfig('save-pack-path'), \RecursiveDirectoryIterator::SKIP_DOTS));
91
                foreach ($it as $dumpLog) {
92
                    if ($zip->addFile($dumpLog, 'pack/'.basename($dumpLog))) {
93
                        array_push($this->removeFiles, $dumpLog);
94
                    }
95
                }
96
            }
97
            $zip->close();
98
        } else {
99
            if (is_file($logFile) && file_exists($logFile)) {
100
                rename($logFile, $this->getConfig('save-path') . '/' . date('Y-m-d'). '-'. substr(md5_file($logFile), 0, 8).'.log');
101
            }
102
        }
103
    }
104
105
    /**
106
     * 检查日志文件大小
107
     *
108
     * @return boolean
109
     */
110
    protected function checkSize():bool
111
    {
112
        $logFile= $this->latest;
113
        if (file_exists($logFile)) {
114
            if (filesize($logFile) > $this->getConfig('max-file-size')) {
115
                return true;
116
            }
117
        }
118
        return false;
119
    }
120
121
122
    public function log($level, string $message, array $context = [])
123
    {
124
        if (LogLevel::compare($level, $this->getConfig('log-level')) >=0) {
125
            $replace = [];
126
            $message = $this->interpolate($message, $context);
127
            $replace['%level%'] = $level;
128
            $replace['%message%'] = $message;
129
            $write = \strtr($this->getConfig('log-format'), $replace);
130
            \fwrite($this->temp, $write.PHP_EOL);
131
        }
132
    }
133
134
    protected function rollLatest()
135
    {
136
        if (isset($this->latest)) {
137
            $size=ftell($this->temp);
138
            fseek($this->temp, 0);
139
            if ($size > 0) {
140
                $body=fread($this->temp, $size);
141
                file_put_contents($this->latest, $body, FILE_APPEND);
142
            }
143
            fclose($this->temp);
144
            if (isset($this->tempname)) {
145
                unlink($this->tempname);
146
            }
147
        }
148
    }
149
150
    protected function removePackFiles()
151
    {
152
        foreach ($this->removeFiles as $file) {
153
            if (\is_file($file) && \file_exists($file)) {
154
                unlink($file);
155
            }
156
        }
157
    }
158
159
    public function save()
160
    {
161
        if ($this->checkSize()) {
162
            $this->packLogFile();
163
        }
164
        $this->rollLatest();
165
        $this->removePackFiles();
166
    }
167
168
    public function interpolate(string $message, array $context)
169
    {
170
        $replace = [];
171
        foreach ($context as $key => $val) {
172
            if (\is_bool($val)) {
173
                $val = $val ? 'true' : 'false';
174
            } elseif (is_null($val)) {
175
                $val = 'null';
176
            }
177
            $replace['{' . $key . '}'] = $val;
178
        }
179
        return strtr($message, $replace);
180
    }
181
}
182