Passed
Push — master ( c41b44...c53913 )
by Alexander
02:04
created

MessageFormatter::getGlobalContext()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 6
c 0
b 0
f 0
nc 16
nop 2
dl 0
loc 12
ccs 7
cts 7
cp 1
crap 5
rs 9.6111
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Log;
6
7
use DateTime;
8
use RuntimeException;
9
use Yiisoft\VarDumper\VarDumper;
10
11
use function gettype;
12
use function implode;
13
use function is_string;
14
use function microtime;
15
use function sprintf;
16
use function strpos;
17
18
/**
19
 * MessageFormatter formats log messages.
20
 *
21
 * @internal
22
 */
23
final class MessageFormatter
24
{
25
    /**
26
     * @var callable|null PHP callable that returns a string representation of the log message.
27
     *
28
     * If not set, {@see MessageFormatter::defaultFormat()} will be used.
29
     *
30
     * The signature of the callable should be `function (array $message): string;`.
31
     */
32
    private $format;
33
34
    /**
35
     * @var callable|null PHP callable that returns a string to be prefixed to every exported message.
36
     *
37
     * If not set, {@see MessageFormatter::getPrefix()} will be used, which prefixes
38
     * the message with context information such as user IP, user ID and session ID.
39
     *
40
     * The signature of the callable should be `function (array $message): string;`.
41
     */
42
    private $prefix;
43
44
    /**
45
     * @var string The date format for the log timestamp. Defaults to `Y-m-d H:i:s.u`.
46
     */
47
    private string $timestampFormat = 'Y-m-d H:i:s.u';
48
49
    /**
50
     * Sets the format for the string representation of the log message.
51
     *
52
     * @param callable $format The PHP callable to get a string representation of the log message.
53
     *
54
     * @see MessageFormatter::$format
55
     */
56 21
    public function setFormat(callable $format): void
57
    {
58 21
        $this->format = $format;
59 21
    }
60
61
    /**
62
     * Sets a PHP callable that returns a string to be prefixed to every exported message.
63
     *
64
     * @param callable $prefix The PHP callable to get a string prefix of the log message.
65
     *
66
     * @see MessageFormatter::$prefix
67
     */
68 18
    public function setPrefix(callable $prefix): void
69
    {
70 18
        $this->prefix = $prefix;
71 18
    }
72
73
    /**
74
     * Sets a date format for the log timestamp.
75
     *
76
     * @param string $timestampFormat The date format for the log timestamp.
77
     *
78
     * @see MessageFormatter::$timestampFormat
79
     */
80 4
    public function setTimestampFormat(string $timestampFormat): void
81
    {
82 4
        $this->timestampFormat = $timestampFormat;
83 4
    }
84
85
    /**
86
     * Formats a log message for display as a string.
87
     *
88
     * The message structure follows that in {@see MessageCollection::$messages}.
89
     *
90
     * @param array $message The log message to be formatted.
91
     *
92
     * @throws RuntimeException for a callable "format" that does not return a string.
93
     *
94
     * @return string The formatted log message.
95
     */
96 42
    public function format(array $message): string
97
    {
98 42
        if ($this->format === null) {
99 22
            return $this->defaultFormat($message);
100
        }
101
102 20
        $formatted = ($this->format)($message);
103
104 20
        if (!is_string($formatted)) {
105 12
            throw new RuntimeException(sprintf(
106 12
                'The PHP callable "format" must return a string, %s received.',
107 12
                gettype($formatted)
108
            ));
109
        }
110
111 8
        return $this->getPrefix($message) . $formatted;
112
    }
113
114
    /**
115
     * Default formats a log message for display as a string.
116
     *
117
     * The message structure follows that in {@see MessageCollection::$messages}.
118
     *
119
     * @param array $message The log message to be default formatted.
120
     *
121
     * @return string The default formatted log message.
122
     */
123 22
    private function defaultFormat(array $message): string
124
    {
125 22
        [$level, $text, $context] = $message;
126 22
        $prefix = $this->getPrefix($message);
127 10
        $category = $context['category'] ?? MessageCategoryFilter::DEFAULT;
128
129 10
        $timestamp = $this->getTime($context['time'] ?? microtime(true));
130 10
        $trace = $this->getTrace($context['trace'] ?? []);
131 10
        $globalContext = $this->getGlobalContext($context['globals'] ?? [], $context['params'] ?? []);
132
133 10
        return "{$timestamp} {$prefix}[{$level}][{$category}] {$text}{$trace}{$globalContext}";
134
    }
135
136
    /**
137
     * Gets a string to be prefixed to the given message.
138
     *
139
     * If {@see MessageFormatter::$prefix} is configured it will return the result of the callback.
140
     * The default implementation will return user IP, user ID and session ID as a prefix.
141
     * The message structure follows that in {@see MessageCollection::$messages}.
142
     *
143
     * @param array $message The log message being exported.
144
     *
145
     * @throws RuntimeException for a callable "prefix" that does not return a string.
146
     *
147
     * @return string The log prefix string.
148
     */
149 30
    private function getPrefix(array $message): string
150
    {
151 30
        if ($this->prefix === null) {
152 12
            return '';
153
        }
154
155 18
        $prefix = ($this->prefix)($message);
156
157 18
        if (!is_string($prefix)) {
158 12
            throw new RuntimeException(sprintf(
159 12
                'The PHP callable "prefix" must return a string, %s received.',
160 12
                gettype($prefix)
161
            ));
162
        }
163
164 6
        return $prefix;
165
    }
166
167
    /**
168
     * @param array $traces Debug backtrace, contains the application code call stacks.
169
     *
170
     * @return string Debug backtrace in string representation.
171
     */
172 10
    private function getTrace(array $traces): string
173
    {
174 10
        foreach ($traces as $key => $trace) {
175 1
            if (isset($trace['file'], $trace['line'])) {
176 1
                $traces[$key] = "in {$trace['file']}:{$trace['line']}";
177
            }
178
        }
179
180 10
        return empty($traces) ? '' : "\n    " . implode("\n    ", $traces);
181
    }
182
183
    /**
184
     * Generates the global context information to be logged.
185
     *
186
     * @param array $globals List of the predefined PHP global variables.
187
     * @param array $params List of user parameters in the `key => value` format.
188
     *
189
     * @return string The global context information. If an empty string, it means no context information.
190
     */
191 10
    private function getGlobalContext(array $globals, array $params): string
192
    {
193 10
        foreach ($globals as $key => $value) {
194 2
            $globals[$key] = "\${$key} = " . VarDumper::create($value)->asString();
195
        }
196
197 10
        foreach ($params as $key => $value) {
198 2
            $params[$key] = "{$key} = " . VarDumper::create($value)->asString();
199
        }
200
201 10
        return (empty($params) ? '' : "\n\nUser parameters:\n\n" . implode("\n\n", $params))
202 10
            . (empty($globals) ? '' : "\n\nGlobal variables:\n\n" . implode("\n\n", $globals));
203
    }
204
205
    /**
206
     * Gets formatted timestamp for message, according to {@see MessageFormatter::$timestampFormat}.
207
     *
208
     * @param float|int $timestamp The timestamp to be formatted.
209
     *
210
     * @return string Formatted timestamp for message.
211
     */
212 10
    private function getTime($timestamp): string
213
    {
214 10
        $timestamp = (string) $timestamp;
215 10
        $format = strpos($timestamp, '.') === false ? 'U' : 'U.u';
216 10
        return DateTime::createFromFormat($format, $timestamp)->format($this->timestampFormat);
217
    }
218
}
219