Test Failed
Pull Request — master (#12)
by wujunze
03:09
created

LoggerConfig   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 196
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 123
c 2
b 0
f 1
dl 0
loc 196
rs 8.96
wmc 43

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 3
A flush() 0 9 6
A getDatetimeFormat() 0 3 1
A setDatetimeFormat() 0 7 2
F log() 0 93 30
A getBuffer() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like LoggerConfig often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use LoggerConfig, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
4
namespace Seasx\SeasLogger;
5
6
use Co;
7
use Exception;
8
use Psr\Log\InvalidArgumentException;
9
10
/**
11
 * Class LoggerConfig
12
 * @package Seasx\SeasLogger
13
 */
14
class LoggerConfig extends AbstractConfig
15
{
16
    /** @var array */
17
    protected $buffer = [];
18
    /** @var string */
19
    protected $datetime_format = "Y-m-d H:i:s";
20
    /** @var array */
21
    private static $supportTemplate = [
22
        '%W',
23
        '%L',
24
        '%M',
25
        '%T',
26
        '%t',
27
        '%Q',
28
        '%H',
29
        '%P',
30
        '%D',
31
        '%R',
32
        '%m',
33
        '%I',
34
        '%F',
35
        '%U',
36
        '%u',
37
        '%C',
38
        '%A'
39
    ];
40
    /** @var int */
41
    protected $isMicroTime = 3;
42
    /** @var bool */
43
    protected $useBasename = true;
44
45
    /**
46
     * LoggerConfig constructor.
47
     * @param array $target
48
     * @param array $template
49
     * @param array $configs
50
     */
51
    public function __construct(
52
        array $target,
53
        array $configs = [],
54
        array $template = ['%T', '%L', '%R', '%m', '%I', '%Q', '%F', '%U', '%A', '%M']
55
    ) {
56
        foreach ($template as $tmp) {
57
            if (!in_array($tmp, self::$supportTemplate)) {
58
                throw new InvalidArgumentException("$tmp not supported!");
59
            }
60
        }
61
        $this->template = $template;
62
        parent::__construct($target, $configs);
63
    }
64
65
    /**
66
     * @return string
67
     */
68
    public function getDatetimeFormat(): string
69
    {
70
        return $this->datetime_format;
71
    }
72
73
    /**
74
     * @param string $format
75
     * @return bool
76
     */
77
    public function setDatetimeFormat(string $format): bool
78
    {
79
        if (date($format, time()) !== false) {
80
            $this->datetime_format = $format;
81
            return true;
82
        }
83
        return false;
84
    }
85
86
    /**
87
     * 获得当前日志buffer中的内容.
88
     *
89
     * @return array
90
     */
91
    public function getBuffer(): array
92
    {
93
        return $this->buffer;
94
    }
95
96
    /**
97
     * @param string $level
98
     * @param string $message
99
     * @param array $context
100
     * @throws Exception
101
     */
102
    public function log(string $level, string $message, array $context = []): void
103
    {
104
        $template = $this->getUserTemplate();
105
        $msg = [];
106
        $module = ArrayHelper::getValue($context, 'module');
107
        foreach ($this->template as $tmp) {
108
            switch ($tmp) {
109
                case '%W':
110
                    $msg[] = ArrayHelper::getValue($template, $tmp, -1);
111
                    break;
112
                case '%L':
113
                    $msg[] = $level;
114
                    break;
115
                case '%M':
116
                    $msg[] = str_replace($this->split, ' ', empty($context) ? $message : strtr($message, $context));
117
                    break;
118
                case '%T':
119
                case '%t':
120
                    if ($this->isMicroTime > 0) {
121
                        $micsec = $this->isMicroTime > 3 ? 3 : $this->isMicroTime;
122
                        $mtimestamp = sprintf("%.{$micsec}f", microtime(true)); // 带毫秒的时间戳
123
                        $timestamp = floor($mtimestamp); // 时间戳
0 ignored issues
show
Bug introduced by
$mtimestamp of type string is incompatible with the type double expected by parameter $value of floor(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

123
                        $timestamp = floor(/** @scrutinizer ignore-type */ $mtimestamp); // 时间戳
Loading history...
124
                        $milliseconds = round(($mtimestamp - $timestamp) * 1000); // 毫秒
125
                    } else {
126
                        $timestamp = time();
127
                        $milliseconds = 0;
128
                    }
129
                    if ($tmp === '%T') {
130
                        $msg[] = date($this->datetime_format, (int)$timestamp) . '.' . (int)$milliseconds;
131
                    } else {
132
                        $msg[] = date($this->datetime_format, (int)$timestamp);
133
                    }
134
                    break;
135
                case '%Q':
136
                    $msg[] = ArrayHelper::getValue($template, $tmp, uniqid());
137
                    break;
138
                case '%H':
139
                    $msg[] = ArrayHelper::getValue($template, $tmp, $_SERVER['HOSTNAME']);
140
                    break;
141
                case '%P':
142
                    $msg[] = ArrayHelper::getValue($template, $tmp, getmypid());
143
                    break;
144
                case '%D':
145
                    $msg[] = ArrayHelper::getValue($template, $tmp, 'cli');
146
                    break;
147
                case '%R':
148
                    $msg[] = ArrayHelper::getValue($template, $tmp, $_SERVER['SCRIPT_NAME']);
149
                    break;
150
                case '%m':
151
                    $method = ArrayHelper::getValue($template, $tmp);
152
                    $msg[] = $method ? strtoupper($method) : $_SERVER['SHELL'];
153
                    break;
154
                case '%I':
155
                    $msg[] = ArrayHelper::getValue($template, $tmp, 'local');
156
                    break;
157
                case '%F':
158
                case '%C':
159
                    $trace = Co::getBackTrace(Co::getCid(), DEBUG_BACKTRACE_IGNORE_ARGS,
0 ignored issues
show
Bug introduced by
Are you sure the usage of Co::getCid() targeting Swoole\Coroutine::getCid() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure the assignment to $trace is correct as Co::getBackTrace(Co::get...this->recall_depth + 2) targeting Swoole\Coroutine::getBackTrace() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Seasx\SeasLogger\DEBUG_BACKTRACE_IGNORE_ARGS of type integer is incompatible with the type array|null expected by parameter $options of Swoole\Coroutine::getBackTrace(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

159
                    $trace = Co::getBackTrace(Co::getCid(), /** @scrutinizer ignore-type */ DEBUG_BACKTRACE_IGNORE_ARGS,
Loading history...
160
                        $this->recall_depth + 2);
161
                    if ($tmp === '%F') {
162
                        $trace = $trace[$this->recall_depth];
163
                        $msg[] = $this->useBasename ? basename($trace['file']) . ':' . $trace['line'] : $trace['file'] . ':' . $trace['line'];
164
                    } else {
165
                        $trace = $trace[$this->recall_depth + 1];
166
                        $msg[] = $trace['class'] . $trace['type'] . $trace['function'];
167
                    }
168
                    break;
169
                case '%U':
170
                    $msg[] = memory_get_usage();
171
                    break;
172
                case '%u':
173
                    $msg[] = memory_get_peak_usage();
174
                    break;
175
                case '%A':
176
                    $customerTemplate = ArrayHelper::getValue($context, 'template',
177
                            []) ?? ArrayHelper::getValue($template, $tmp,
178
                            []);
179
                    switch ($this->customerType) {
180
                        case AbstractConfig::TYPE_JSON:
181
                            $msg[] = json_encode($customerTemplate, JSON_UNESCAPED_UNICODE);
182
                            break;
183
                        case AbstractConfig::TYPE_FIELD:
184
                        default:
185
                            $msg[] = implode($this->split, $customerTemplate);
0 ignored issues
show
Bug introduced by
It seems like $customerTemplate can also be of type null; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

185
                            $msg[] = implode($this->split, /** @scrutinizer ignore-type */ $customerTemplate);
Loading history...
186
                    }
187
                    break;
188
            }
189
        }
190
        $color = ArrayHelper::getValue($template, '%c');
191
        $color && $msg['%c'] = $color;
192
        $key = $this->appName . ($module ? '_' . $module : '');
193
        $this->buffer[$key][] = $msg;
194
        $this->flush();
195
    }
196
197
    /**
198
     * @param bool $flush
199
     * @throws Exception
200
     */
201
    public function flush(bool $flush = false): void
202
    {
203
        if (!empty($this->buffer) && $flush || ($this->bufferSize !== 0 && $this->bufferSize <= count($this->buffer))) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($this->buffer) ...<= count($this->buffer), Probably Intended Meaning: ! empty($this->buffer) &...= count($this->buffer))
Loading history...
204
            foreach ($this->targetList as $index => $target) {
205
                rgo(function () use ($target, $flush) {
0 ignored issues
show
Unused Code introduced by
The import $flush is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
206
                    $target->export($this->buffer);
207
                });
208
            }
209
            array_splice($this->buffer, 0);
210
        }
211
    }
212
}