Completed
Push — 2.1 ( 4d9204...3e6f8b )
by
unknown
11:56
created

Logger::log()   D

Complexity

Conditions 18
Paths 482

Size

Total Lines 59
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 26.5139

Importance

Changes 0
Metric Value
dl 0
loc 59
ccs 26
cts 37
cp 0.7027
rs 4.3136
c 0
b 0
f 0
cc 18
eloc 38
nc 482
nop 3
crap 26.5139

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\log;
9
10
use Psr\Log\InvalidArgumentException;
11
use Psr\Log\LoggerInterface;
12
use Psr\Log\LogLevel;
13
use Psr\Log\LoggerTrait;
14
use Yii;
15
use yii\base\Component;
16
use yii\base\ErrorHandler;
17
18
/**
19
 * Logger records logged messages in memory and sends them to different targets according to [[targets]].
20
 *
21
 * A Logger instance can be accessed via `Yii::getLogger()`. You can call the method [[log()]] to record a single log message.
22
 * For convenience, a set of shortcut methods are provided for logging messages of various severity levels
23
 * via the [[Yii]] class:
24
 *
25
 * - [[Yii::debug()]]
26
 * - [[Yii::error()]]
27
 * - [[Yii::warning()]]
28
 * - [[Yii::info()]]
29
 *
30
 * For more details and usage information on Logger, see the [guide article on logging](guide:runtime-logging)
31
 * and [PSR-3 specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md).
32
 *
33
 * When the application ends or [[flushInterval]] is reached, Logger will call [[flush()]]
34
 * to send logged messages to different log targets, such as [[FileTarget|file]], [[EmailTarget|email]],
35
 * or [[DbTarget|database]], according to the [[targets]].
36
 *
37
 * @property array|Target[] $targets the log targets. See [[setTargets()]] for details.
38
 * @property float $elapsedTime The total elapsed time in seconds for current request. This property is
39
 * read-only.
40
 *
41
 * @author Qiang Xue <[email protected]>
42
 * @since 2.0
43
 */
44
class Logger extends Component implements LoggerInterface
45
{
46
    use LoggerTrait;
47
48
    /**
49
     * @var array logged messages. This property is managed by [[log()]] and [[flush()]].
50
     * Each log message is of the following structure:
51
     *
52
     * ```
53
     * [
54
     *   [0] => level (string)
55
     *   [1] => message (mixed, can be a string or some complex data, such as an exception object)
56
     *   [2] => context (array)
57
     * ]
58
     * ```
59
     *
60
     * Message context has a following keys:
61
     *
62
     * - category: string, message category.
63
     * - time: float, message timestamp obtained by microtime(true).
64
     * - trace: array, debug backtrace, contains the application code call stacks.
65
     * - memory: int, memory usage in bytes, obtained by `memory_get_usage()`, available since version 2.0.11.
66
     */
67
    public $messages = [];
68
    /**
69
     * @var int how many messages should be logged before they are flushed from memory and sent to targets.
70
     * Defaults to 1000, meaning the [[flush]] method will be invoked once every 1000 messages logged.
71
     * Set this property to be 0 if you don't want to flush messages until the application terminates.
72
     * This property mainly affects how much memory will be taken by the logged messages.
73
     * A smaller value means less memory, but will increase the execution time due to the overhead of [[flush()]].
74
     */
75
    public $flushInterval = 1000;
76
    /**
77
     * @var int how much call stack information (file name and line number) should be logged for each message.
78
     * If it is greater than 0, at most that number of call stacks will be logged. Note that only application
79
     * call stacks are counted.
80
     */
81
    public $traceLevel = 0;
82
83
    /**
84
     * @var array|Target[] the log targets. Each array element represents a single [[Target|log target]] instance
85
     * or the configuration for creating the log target instance.
86
     * @since 2.1
87
     */
88
    private $_targets = [];
89
    /**
90
     * @var bool whether [[targets]] have been initialized, e.g. ensured to be objects.
91
     * @since 2.1
92
     */
93
    private $_isTargetsInitialized = false;
94
95
96
    /**
97
     * @return Target[] the log targets. Each array element represents a single [[Target|log target]] instance.
98
     * @since 2.1
99
     */
100 35
    public function getTargets()
101
    {
102 35
        if (!$this->_isTargetsInitialized) {
103 27
            foreach ($this->_targets as $name => $target) {
104 27
                if (!$target instanceof Target) {
105 27
                    $this->_targets[$name] = Yii::createObject($target);
106
                }
107
            }
108 27
            $this->_isTargetsInitialized = true;
109
        }
110 35
        return $this->_targets;
111
    }
112
113
    /**
114
     * @param array|Target[] $targets the log targets. Each array element represents a single [[Target|log target]] instance
115
     * or the configuration for creating the log target instance.
116
     * @since 2.1
117
     */
118 27
    public function setTargets($targets)
119
    {
120 27
        $this->_targets = $targets;
121 27
        $this->_isTargetsInitialized = false;
122 27
    }
123
124
    /**
125
     * Adds extra target to [[targets]].
126
     * @param Target|array $target the log target instance or its DI compatible configuration.
127
     * @param string|null $name array key to be used to store target, if `null` is given target will be append
128
     * to the end of the array by natural integer key.
129
     */
130 1
    public function addTarget($target, $name = null)
131
    {
132 1
        if (!$target instanceof Target) {
133
            $this->_isTargetsInitialized = false;
134
        }
135 1
        if ($name === null) {
136 1
            $this->_targets[] = $target;
137
        } else {
138 1
            $this->_targets[$name] = $target;
139
        }
140 1
    }
141
142
    /**
143
     * Initializes the logger by registering [[flush()]] as a shutdown function.
144
     */
145 21
    public function init()
146
    {
147 21
        parent::init();
148 21
        register_shutdown_function(function () {
149
            // make regular flush before other shutdown functions, which allows session data collection and so on
150
            $this->flush();
151
            // make sure log entries written by shutdown functions are also flushed
152
            // ensure "flush()" is called last when there are multiple shutdown functions
153
            register_shutdown_function([$this, 'flush'], true);
154 21
        });
155 21
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160 1536
    public function log($level, $message, array $context = array())
161
    {
162 1536
        if (!is_string($message)) {
163
            if (is_scalar($message)) {
164
                $message = (string)$message;
165
            } elseif (is_object($message)) {
166
                if ($message instanceof \Throwable) {
167
                    if (!isset($context['exception'])) {
168
                        $context['exception'] = $message;
169
                    }
170
                    $message = $message->__toString();
171
                } elseif (method_exists($message, '__toString')) {
172
                    $message = $message->__toString();
173
                } else {
174
                    throw new InvalidArgumentException('The log message MUST be a string or object implementing __toString()');
175
                }
176
            } else {
177
                throw new InvalidArgumentException('The log message MUST be a string or object implementing __toString()');
178
            }
179
        }
180
181 1536
        if (!isset($context['time'])) {
182 1536
            $context['time'] = microtime(true);
183
        }
184 1536
        if (!isset($context['trace'])) {
185 1536
            $traces = [];
186 1536
            if ($this->traceLevel > 0) {
187 1
                $count = 0;
188 1
                $ts = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
189 1
                array_pop($ts); // remove the last trace since it would be the entry script, not very useful
190 1
                foreach ($ts as $trace) {
191 1
                    if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII2_PATH) !== 0) {
192 1
                        unset($trace['object'], $trace['args']);
193 1
                        $traces[] = $trace;
194 1
                        if (++$count >= $this->traceLevel) {
195 1
                            break;
196
                        }
197
                    }
198
                }
199
            }
200 1536
            $context['trace'] = $traces;
201
        }
202
203 1536
        if (!isset($context['memory'])) {
204 1536
            $context['memory'] = memory_get_usage();
205
        }
206
207 1536
        if (!isset($context['category'])) {
208 23
            $context['category'] = 'application';
209
        }
210
211 1536
        $message = $this->parseMessage($message, $context);
212
213 1536
        $this->messages[] = [$level, $message, $context];
214
215 1536
        if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) {
216 28
            $this->flush();
217
        }
218 1536
    }
219
220
    /**
221
     * Flushes log messages from memory to targets.
222
     * @param bool $final whether this is a final call during a request.
223
     */
224 36
    public function flush($final = false)
225
    {
226 36
        $messages = $this->messages;
227
        // https://github.com/yiisoft/yii2/issues/5619
228
        // new messages could be logged while the existing ones are being handled by targets
229 36
        $this->messages = [];
230
231 36
        $this->dispatch($messages, $final);
232 36
    }
233
234
    /**
235
     * Dispatches the logged messages to [[targets]].
236
     * @param array $messages the logged messages
237
     * @param bool $final whether this method is called at the end of the current application
238
     * @since 2.1
239
     */
240 37
    protected function dispatch($messages, $final)
241
    {
242 37
        $targetErrors = [];
243 37
        foreach ($this->targets as $target) {
244 34
            if ($target->enabled) {
245
                try {
246 33
                    $target->collect($messages, $final);
247 1
                } catch (\Exception $e) {
248 1
                    $target->enabled = false;
249 1
                    $targetErrors[] = [
250 34
                        'Unable to send log via ' . get_class($target) . ': ' . ErrorHandler::convertExceptionToString($e),
251
                        LogLevel::WARNING,
252
                        __METHOD__,
253 1
                        microtime(true),
254
                        [],
255
                    ];
256
                }
257
            }
258
        }
259
260 37
        if (!empty($targetErrors)) {
261 1
            $this->dispatch($targetErrors, true);
262
        }
263 37
    }
264
265
    /**
266
     * Parses log message resolving placeholders in the form: '{foo}', where foo
267
     * will be replaced by the context data in key "foo".
268
     * @param string $message log message.
269
     * @param array $context message context.
270
     * @return string parsed message.
271
     * @since 2.1
272
     */
273
    protected function parseMessage($message, array $context)
274
    {
275 1536
        return preg_replace_callback('/\\{([\\w\\.]+)\\}/is', function ($matches) use ($context) {
276 8
            $placeholderName = $matches[1];
277 8
            if (isset($context[$placeholderName])) {
278 1
                return (string)$context[$placeholderName];
279
            }
280 7
            return $matches[0];
281 1536
        }, $message);
282
    }
283
284
    /**
285
     * Returns the total elapsed time since the start of the current request.
286
     * This method calculates the difference between now and the timestamp
287
     * defined by constant `YII_BEGIN_TIME` which is evaluated at the beginning
288
     * of [[\yii\BaseYii]] class file.
289
     * @return float the total elapsed time in seconds for current request.
290
     */
291 1
    public function getElapsedTime()
292
    {
293 1
        return microtime(true) - YII_BEGIN_TIME;
294
    }
295
296
    /**
297
     * Returns the text display of the specified level.
298
     * @param mixed $level the message level, e.g. [[LogLevel::ERROR]], [[LogLevel::WARNING]].
299
     * @return string the text display of the level
300
     */
301 3
    public static function getLevelName($level)
302
    {
303 3
        if (is_string($level)) {
304 3
            return $level;
305
        }
306 1
        return 'unknown';
307
    }
308
}
309