Passed
Push — master ( 4f923e...f2cf92 )
by 世昌
01:48
created

FileLogger::__construct()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 4
nop 1
dl 0
loc 14
rs 9.9332
c 0
b 0
f 0
1
<?php
2
namespace nebula\component\debug\log\logger;
3
4
use ZipArchive;
5
use nebula\component\debug\ConfigTrait;
6
use nebula\component\debug\log\LogLevel;
7
use nebula\component\debug\ConfigInterface;
8
use nebula\component\debug\log\AbstractLogger;
9
use nebula\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->config['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->config['save-path'].'/'.$this->config['file-name'];
63
    }
64
65
    public function getDefaultConfig():array {
66
        return [
67
            'save-path' => './logs',
68
            'save-zip-path' => './logs/zip',
69
            'save-pack-path' => './logs/dump',
70
            'max-file-size' => 2097152,
71
            'file-name' => 'latest.log',
72
            'log-level' => 'debug',
73
            'log-format' => '[%level%] %message%',
74
        ];
75
    }
76
    
77
    protected function packLogFile()
78
    {
79
        $logFile= $this->latest;
80
        $path=preg_replace('/[\\\\]+/', '/', $this->config['save-zip-path'] .'/'.date('Y-m-d').'.zip');
81
        $zip = new ZipArchive;
82
        $res = $zip->open($path, ZipArchive::CREATE);
83
        if ($res === true) {
84
            if ($zip->addFile($logFile, date('Y-m-d'). '-'. $zip->numFiles .'.log')) {
85
                array_push($this->removeFiles, $logFile);
86
            }
87
            $it = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->config['save-pack-path']));
88
            foreach ($it as $dumpLog) {
89
                if ($zip->addFile($dumpLog, 'pack/'.basename($dumpLog))) {
90
                    array_push($this->removeFiles, $dumpLog);
91
                }
92
            }
93
            $zip->close();
94
        } else {
95
            if (is_file($logFile) && file_exists($logFile)) {
96
                rename($logFile, $this->config['save-path'] . '/' . date('Y-m-d'). '-'. substr(md5_file($logFile), 0, 8).'.log');
97
            }
98
        }
99
    }
100
101
    /**
102
     * 检查日志文件大小
103
     *
104
     * @return boolean
105
     */
106
    protected function checkSize():bool
107
    {
108
        $logFile= $this->latest;
109
        if (file_exists($logFile)) {
110
            if (filesize($logFile) > $this->config['max-file-size']) {
111
                return true;
112
            }
113
        }
114
        return false;
115
    }
116
117
118
    public function log($level, string $message, array $context = [])
119
    {
120
        if (LogLevel::compare($level, $this->config['log-level']) >=0) {
121
            $replace = [];
122
            $message = $this->interpolate($message, $context);
123
            $replace['%level%'] = $level;
124
            $replace['%message%'] = $message;
125
            $write = \strtr($this->config['log-format'], $replace);
126
            \fwrite($this->temp, $write.PHP_EOL);
127
        }
128
    }
129
130
131
    public function interpolate(string $message, array $context)
132
    {
133
        $replace = [];
134
        foreach ($context as $key => $val) {
135
            $replace['{' . $key . '}'] = $val;
136
        }
137
        return strtr($message, $replace);
138
    }
139
140
    protected function rollLatest()
141
    {
142
        if (isset($this->latest)) {
143
            $size=ftell($this->temp);
144
            fseek($this->temp, 0);
145
            if ($size > 0) {
146
                $body=fread($this->temp, $size);
147
                file_put_contents($this->latest, $body, FILE_APPEND);
148
            }
149
            fclose($this->temp);
150
            if (isset($this->tempname)) {
151
                unlink($this->tempname);
152
            }
153
        }
154
    }
155
156
    protected function removePackFiles()
157
    {
158
        foreach ($this->removeFiles as $file) {
159
            if (\is_file($file) && \file_exists($file)) {
160
                unlink($file);
161
            }
162
        }
163
    }
164
165
    public function save()
166
    {
167
        if ($this->checkSize()) {
168
            $this->packLogFile();
169
        }
170
        $this->rollLatest();
171
        $this->removePackFiles();
172
    }
173
}
174