Passed
Pull Request — master (#36)
by
unknown
02:15
created

PsrTarget   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 78
c 7
b 0
f 0
dl 0
loc 190
rs 10
wmc 26

7 Methods

Rating   Name   Duplication   Size   Complexity  
A filterMessages() 0 6 1
A getLevels() 0 3 1
A setLogger() 0 3 1
A getLogger() 0 11 3
A __construct() 0 4 1
C export() 0 42 12
B setLevels() 0 30 7
1
<?php
2
namespace samdark\log;
3
4
use Closure;
5
use Psr\Log\LoggerAwareInterface;
6
use Psr\Log\LoggerInterface;
7
use Psr\Log\LogLevel;
8
use yii\base\InvalidConfigException;
9
use yii\helpers\VarDumper;
10
use yii\log\Logger;
11
use yii\log\Target;
12
13
/**
14
 * PsrTarget is a log target which passes messages to PSR-3 compatible logger.
15
 *
16
 * @author Alexander Makarov <[email protected]>
17
 */
18
class PsrTarget extends Target implements LoggerAwareInterface
19
{
20
    /**
21
     * The logger instance.
22
     *
23
     * @var LoggerInterface|Closure|null
24
     */
25
    public $logger;
26
27
    /**
28
     * @var bool If enabled, logger use original timestamp from buffer
29
     * @since 1.1.0
30
     */
31
    public $addTimestampToContext = false;
32
33
    /**
34
     * @var bool If enabled, exception's trace will extract into `trace` property
35
     */
36
    public $extractExceptionTrace = false;
37
38
    private $_levelMap = [
39
        Logger::LEVEL_ERROR => LogLevel::ERROR,
40
        Logger::LEVEL_WARNING => LogLevel::WARNING,
41
        Logger::LEVEL_INFO => LogLevel::INFO,
42
        Logger::LEVEL_TRACE => LogLevel::DEBUG,
43
        Logger::LEVEL_PROFILE => LogLevel::DEBUG,
44
        Logger::LEVEL_PROFILE_BEGIN => LogLevel::DEBUG,
45
        Logger::LEVEL_PROFILE_END => LogLevel::DEBUG,
46
47
        // Psr Levels
48
        LogLevel::EMERGENCY => LogLevel::EMERGENCY,
49
        LogLevel::ALERT => LogLevel::ALERT,
50
        LogLevel::CRITICAL => LogLevel::CRITICAL,
51
        LogLevel::ERROR => LogLevel::ERROR,
52
        LogLevel::WARNING => LogLevel::WARNING,
53
        LogLevel::NOTICE => LogLevel::NOTICE,
54
        LogLevel::INFO => LogLevel::INFO,
55
        LogLevel::DEBUG => LogLevel::DEBUG,
56
    ];
57
58
    /**
59
     * @var array
60
     */
61
    private $_levels = [];
62
63
    public function __construct($config = [])
64
    {
65
        $this->_levels = $this->_levelMap;
66
        parent::__construct($config);
67
    }
68
    
69
    /**
70
     * Sets a logger.
71
     *
72
     * @param LoggerInterface $logger
73
     */
74
    public function setLogger(LoggerInterface $logger)
75
    {
76
        $this->logger = $logger;
77
    }
78
    
79
    /**
80
     * @return LoggerInterface
81
     * @throws InvalidConfigException
82
     */
83
    public function getLogger()
84
    {
85
        if ($this->logger === null) {
86
            throw new InvalidConfigException('Logger should be configured with Psr\Log\LoggerInterface.');
87
        }
88
        if ($this->logger instanceof Closure) {
89
            $loggerClosure = $this->logger;
90
            $this->logger = $loggerClosure();
91
        }
92
        
93
        return $this->logger;
94
    }
95
96
    /**
97
     * @inheritdoc
98
     */
99
    public function export()
100
    {
101
        foreach ($this->messages as $message) {
102
            $level = $message[1];
103
104
            $context = [];
105
            if (isset($message[4])) {
106
                $context['trace'] = $message[4];
107
            }
108
109
            if (isset($message[5])) {
110
                $context['memory'] = $message[5];
111
            }
112
113
            if (isset($message[2])) {
114
                $context['category'] = $message[2];
115
            }
116
117
            if ($this->addTimestampToContext && isset($message[3])) {
118
                $context['timestamp'] = $message[3];
119
            }
120
121
            $text = $message[0];
122
            if (!is_string($text)) {
123
                // exceptions may not be serializable if in the call stack somewhere is a Closure
124
                if ($text instanceof \Throwable || $text instanceof \Exception) {
125
                    $context['exception'] = $text;
126
                    if ($this->extractExceptionTrace) {
127
                        $context['trace'] = explode(PHP_EOL, $text->getTraceAsString());
128
                        $text = $text->getMessage();
129
                    } else {
130
                        $text = (string)$text;
131
                    }
132
                } elseif ($text instanceof PsrMessage) {
133
                    $context = array_merge($text->getContext(), $context); // Will not replace standard context keys
134
                    $text = $text->getMessage();
135
                } else {
136
                    $text = VarDumper::export($text);
137
                }
138
            }
139
140
            $this->getLogger()->log($this->_levelMap[$level], $text, $context);
141
        }
142
    }
143
144
    public static function filterMessages($messages, $levels = [], $categories = [], $except = [])
145
    {
146
        $filterByLevel = function ($message) use ($levels) {
147
            return isset($levels[$message[1]]);
148
        };
149
        return array_filter(parent::filterMessages($messages, 0, $categories, $except), $filterByLevel);
150
    }
151
152
    public function getLevels()
153
    {
154
        return $this->_levels;
155
    }
156
157
    /**
158
     * Sets the message levels that this target is interested in.
159
     *
160
     * The parameter can be an array.
161
     * Valid level names include: 'error',
162
     * 'warning', 'info', 'trace' and 'profile'; valid level values include:
163
     * [[Logger::LEVEL_ERROR]], [[Logger::LEVEL_WARNING]], [[Logger::LEVEL_INFO]],
164
     * [[Logger::LEVEL_TRACE]], [[Logger::LEVEL_PROFILE]] and Psr Log levels:
165
     * [[LogLevel::EMERGENCY]], [[LogLevel::ALERT]], [[LogLevel::CRITICAL]],
166
     * [[LogLevel::ERROR]], [[LogLevel::WARNING]], [[LogLevel::NOTICE]],
167
     * [[LogLevel::INFO]] and [[LogLevel::DEBUG]].
168
     *
169
     * For example,
170
     *
171
     * ```php
172
     * ['error', 'warning', LogLevel::CRITICAL, LogLevel::EMERGENCY]
173
     * ```
174
     *
175
     * @param array $levels message levels that this target is interested in.
176
     * @throws InvalidConfigException if $levels value is not correct.
177
     */
178
    public function setLevels($levels)
179
    {
180
        static $levelMap = [
181
            'error' => Logger::LEVEL_ERROR,
182
            'warning' => Logger::LEVEL_WARNING,
183
            'info' => Logger::LEVEL_INFO,
184
            'trace' => Logger::LEVEL_TRACE,
185
            'profile' => Logger::LEVEL_PROFILE,
186
        ];
187
188
        if (is_array($levels)) {
0 ignored issues
show
introduced by
The condition is_array($levels) is always true.
Loading history...
189
            $intrestingLevels = [];
190
            
191
            foreach ($levels as $level) {
192
                if (!isset($this->_levels[$level]) && !isset($levelMap[$level])) {
193
                    throw new InvalidConfigException("Unrecognized level: $level");
194
                }
195
196
                if (isset($levelMap[$level])) {
197
                    $intrestingLevels[$levelMap[$level]] = $this->_levels[$levelMap[$level]];
198
                }
199
200
                if (isset($this->_levels[$level])) {
201
                    $intrestingLevels[$level] = $this->_levels[$level];
202
                }
203
            }
204
            
205
            $this->_levels = $intrestingLevels;
206
        } else {
207
            throw new InvalidConfigException("Incorrect $levels value");
208
        }
209
    }
210
}
211