Passed
Push — master ( 102376...acba12 )
by 世昌
03:37
created

Debug::getTiming()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace suda\framework\debug;
4
5
use Psr\Log\LoggerAwareInterface;
6
use Psr\Log\LoggerAwareTrait;
7
use Psr\Log\LoggerInterface;
8
use Psr\Log\LoggerTrait;
9
use Psr\Log\LogLevel;
10
use suda\framework\debug\attach\DumpTrait;
11
use suda\framework\debug\attach\AttachTrait;
12
use suda\framework\debug\attach\DumpInterface;
13
use suda\framework\debug\attach\AttachInterface;
14
15
class Debug implements LoggerInterface, LoggerAwareInterface, DumpInterface, AttachInterface, ConfigInterface
16
{
17
    use LoggerTrait, LoggerAwareTrait, DumpTrait, AttachTrait, ConfigTrait;
18
19
    /**
20
     * @var array
21
     */
22
    protected static $levels = ['debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency'];
23
24
    /**
25
     * 忽略堆栈
26
     *
27
     * @var array
28
     */
29
    protected $ignoreTraces = [__DIR__];
30
31
    /**
32
     * 时间记录
33
     *
34
     * @var array
35
     */
36
    protected $timeRecord;
37
38
    /**
39
     * @var array
40
     */
41
    protected $timing;
42
43
    /**
44
     * @param mixed $level
45
     * @param string $message
46
     * @param array $context
47
     */
48
    public function log($level, $message, array $context = [])
49
    {
50
        $attribute = $this->getAttribute();
51
        list($attach, $replace) = $this->analyse($message, $context);
52
        $attribute = array_merge($attribute, $attach);
53
        $attribute['message'] = strtr($message, $replace);
54
        $attribute['level'] = $level;
55
        $caller = new Caller(debug_backtrace(), $this->getIgnoreTraces());
56
        $trace = $caller->getCallerTrace();
57
        $attribute['file'] = $trace['file'];
58
        $attribute['line'] = $trace['line'];
59
        $attribute = $this->assignAttributes($attribute);
60
        $this->logger->log($level, $this->interpolate($this->getConfig('log-format'), $attach, $attribute), []);
61
    }
62
63
    /**
64
     * @param string $message
65
     * @param array $context
66
     * @return array
67
     */
68
    public function analyse(string $message, array $context)
69
    {
70
        $replace = [];
71
        $attach = [];
72
        foreach ($context as $key => $val) {
73
            $replaceKey = '{' . $key . '}';
74
            if ($this->canBeStringValue($val) && strpos($message, $replaceKey) !== false) {
75
                $replace['{' . $key . '}'] = $val;
76
            } else {
77
                $attach[$key] = $val;
78
            }
79
        }
80
        return [$attach, $replace];
81
    }
82
83
    /**
84
     * 设置忽略前缀
85
     *
86
     * @return array
87
     */
88
    public function getIgnoreTraces(): array
89
    {
90
        return $this->ignoreTraces;
91
    }
92
93
    /**
94
     * @return array
95
     */
96
    public function getDefaultConfig(): array
97
    {
98
        return [
99
            'log-format' => '%time-format% - %memory-format% [%level%] %file%:%line% %message%',
100
            'start-time' => 0,
101
            'start-memory' => 0,
102
        ];
103
    }
104
105
    /**
106
     * @param $val
107
     * @return bool
108
     */
109
    protected function canBeStringValue($val): bool
110
    {
111
        return !is_array($val) && (!is_object($val) || method_exists($val, '__toString'));
112
    }
113
114
    /**
115
     * @param array $attribute
116
     * @return array
117
     */
118
    protected function assignAttributes(array $attribute): array
119
    {
120
        $attribute['current-time'] = number_format(microtime(true), 4, '.', '');
121
        $time = microtime(true) - $this->getConfig('start-time');
122
        $memory = memory_get_usage() - $this->getConfig('start-memory');
123
        $attribute['time-format'] = number_format($time, 10, '.', '');
124
        $attribute['memory-format'] = $this->formatBytes($memory, 2);
125
        $attribute['memory'] = $memory;
126
        return $attribute;
127
    }
128
129
    /**
130
     * @param int $bytes
131
     * @param int $precision
132
     * @return string
133
     */
134
    public static function formatBytes(int $bytes, int $precision = 0)
135
    {
136
        $human = ['B', 'KB', 'MB', 'GB', 'TB'];
137
        $bytes = max($bytes, 0);
138
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
139
        $pos = min($pow, count($human) - 1);
140
        $bytes /= (1 << (10 * $pos));
141
        return round($bytes, $precision) . ' ' . $human[$pos];
142
    }
143
144
    /**
145
     * @param string $name
146
     * @param string $type
147
     */
148
    public function time(string $name, string $type = LogLevel::INFO)
149
    {
150
        $this->timeRecord[$name] = ['time' => microtime(true), 'level' => $type];
151
    }
152
153
    /**
154
     * @param string $name
155
     * @return float
156
     */
157
    public function timeEnd(string $name)
158
    {
159
        if (array_key_exists($name, $this->timeRecord)) {
160
            $pass = microtime(true) - $this->timeRecord[$name]['time'];
161
            $this->log(
162
                $this->timeRecord[$name]['level'],
163
                sprintf("process %s cost %ss", $name, number_format($pass, 5, '.', ''))
164
            );
165
            unset($this->timeRecord[$name]);
166
            return $pass;
167
        }
168
        return 0;
169
    }
170
171
    /**
172
     * @param string $name
173
     * @param float $time
174
     * @param string $description
175
     */
176
    public function recordTiming(string $name, float $time, string $description = '')
177
    {
178
        if (array_key_exists($name, $this->timing)) {
179
            $this->timing[$name]['time'] += $time;
180
        } else {
181
            $this->timing[$name]['time'] = $time;
182
        }
183
        $this->timing[$name]['description'] = $description;
184
    }
185
186
    /**
187
     * @return array
188
     */
189
    public function getTiming(): array
190
    {
191
        return $this->timing;
192
    }
193
194
    /**
195
     * @param array $ignoreTraces
196
     * @return $this
197
     */
198
    public function setIgnoreTraces(array $ignoreTraces)
199
    {
200
        $this->ignoreTraces = $ignoreTraces;
201
        return $this;
202
    }
203
204
205
    /**
206
     * 比较优先级
207
     *
208
     * @param string $a
209
     * @param string $b
210
     * @return integer
211
     */
212
    public static function compare(string $a, string $b): int
213
    {
214
        $indexA = array_search($a, static::$levels);
215
        $indexB = array_search($b, static::$levels);
216
        return $indexA - $indexB;
217
    }
218
219
    /**
220
     * 添加忽略路径
221
     * @param string $path
222
     * @return $this
223
     */
224
    public function addIgnorePath(string $path)
225
    {
226
        $this->ignoreTraces[] = $path;
227
        return $this;
228
    }
229
}
230