Completed
Branch master (1278e5)
by ྅༻ Ǭɀħ
02:01 queued 34s
created

Logger::hasException()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
crap 2
1
<?php
2
3
namespace Ozh\Log;
4
5
use \Psr\Log\LoggerInterface;
6
use \Psr\Log\LogLevel;
7
use \Psr\Log\InvalidArgumentException;
8
9
/**
10
 * Ozh\Log\Logger - a minimalist PSR-3 compliant logger, that logs into an array.
11
 *
12
 * (c) Ozh 2017 - Do whatever the hell you want with it
13
 */
14
15
class Logger implements LoggerInterface
16
{
17
    /**
18
     * The array that will collect all the log messages
19
     *
20
     * @see Logger::log()
21
     * @see Logger::getLog()
22
     * @var array
23
     */
24
    protected $log = array();
25
    
26
27
    /**
28
     * The callable to format every logged message
29
     *
30
     * @var callable
31
     * @see Logger::defaultFormat()
32
     */
33
    protected $message_format;
34
    
35
36
    /**
37
     * Current logging level (eg 'debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert' or 'emergency')
38
     *
39
     * @var string
40
     */
41
    protected $level;
42
43
44
    /**
45
     * Logging level mapping, used to hierarchize
46
     *
47
     * This complies to \Psr\Log\LogLevel and RFC 5424 severity values :
48
     *   0  Emergency: system is unusable
49
     *   1  Alert: action must be taken immediately
50
     *   2  Critical: critical conditions
51
     *   3  Error: error conditions
52
     *   4  Warning: warning conditions
53
     *   5  Notice: normal but significant condition
54
     *   6  Informational: informational messages
55
     *   7  Debug: debug-level messages
56
     *
57
     * @var array
58
     */
59
    protected $log_levels = array(
60
        LogLevel::DEBUG     => 7,
61
        LogLevel::INFO      => 6,
62
        LogLevel::NOTICE    => 5,
63
        LogLevel::WARNING   => 4,
64
        LogLevel::ERROR     => 3,
65
        LogLevel::CRITICAL  => 2,
66
        LogLevel::ALERT     => 1,
67
        LogLevel::EMERGENCY => 0,
68
    );
69
70
71
    /**
72
     * @param  string $level
73
     */
74 75
    public function __construct($level = LogLevel::DEBUG)
75
    {
76 75
        if ($this->isLogLevel($level)) {
77 72
            $this->level = $level;
78 72
            $this->setMessageFormat(array($this, 'defaultFormat'));
79 48
        }
80 72
    }
81
82
83
    /**
84
     * @param  string $message
85
     * @param  array  $context
86
     * @see    Logger::log()
87
     * @return void
88
     */
89 3
    public function emergency($message, array $context = array())
90
    {
91 3
        $this->log(LogLevel::EMERGENCY, $message, $context);
92 3
    }
93
94
95
    /**
96
     * @param  string $message
97
     * @param  array  $context
98
     * @see    Logger::log()
99
     * @return void
100
     */
101 3
    public function alert($message, array $context = array())
102
    {
103 3
        $this->log(LogLevel::ALERT, $message, $context);
104 3
    }
105
106
107
    /**
108
     * @param  string $message
109
     * @param  array  $context
110
     * @see    Logger::log()
111
     * @return void
112
     */
113 3
    public function critical($message, array $context = array())
114
    {
115 3
        $this->log(LogLevel::CRITICAL, $message, $context);
116 3
    }
117
118
119
    /**
120
     * @param  string $message
121
     * @param  array  $context
122
     * @see    Logger::log()
123
     * @return void
124
     */
125 15
    public function error($message, array $context = array())
126
    {
127 15
        $this->log(LogLevel::ERROR, $message, $context);
128 12
    }
129
130
131
    /**
132
     * @param  string $message
133
     * @param  array  $context
134
     * @see    Logger::log()
135
     * @return void
136
     */
137 3
    public function warning($message, array $context = array())
138
    {
139 3
        $this->log(LogLevel::WARNING, $message, $context);
140 3
    }
141
142
143
    /**
144
     * @param  string $message
145
     * @param  array  $context
146
     * @see    Logger::log()
147
     * @return void
148
     */
149 3
    public function notice($message, array $context = array())
150
    {
151 3
        $this->log(LogLevel::NOTICE, $message, $context);
152 3
    }
153
154
155
    /**
156
     * @param  string $message
157
     * @param  array  $context
158
     * @see    Logger::log()
159
     * @return void
160
     */
161 3
    public function info($message, array $context = array())
162
    {
163 3
        $this->log(LogLevel::INFO, $message, $context);
164 3
    }
165
166
    /**
167
     * @param  string $message
168
     * @param  array  $context
169
     * @see    Logger::log()
170
     * @return void
171
     */
172 9
    public function debug($message, array $context = array())
173
    {
174 9
        $this->log(LogLevel::DEBUG, $message, $context);
175 9
    }
176
177
178
    /**
179
     * @param  string $level
180
     * @param  mixed  $message
181
     * @param  array  $context
182
     * @throws InvalidArgumentException
183
     * @return void
184
     */
185 45
    public function log($level, $message, array $context = array())
186
    {
187
        /**
188
         */
189 45
        if ($this->isStringable($message)) {
190 42
            $message = (string) $message;
191 28
        } else {
192 3
            throw new InvalidArgumentException('Message must be a string or an object with a __toString() method');
193
        }
194
195 42
        if ($this->isLogLevel($level) && $this->shouldLog($level)) {
196
            // This doesn't work on PHP 5.3, which throws "PHP Fatal error: Function name must be a string"
197
            // $formatter = $this->getMessageFormat();
198
            // $this->log[] = $formatter($level, $message, $context);
199
            // Instead, the following works on 5.3 to 7.2 & HHVM :
200 39
            $this->log[] = call_user_func_array($this->getMessageFormat(), array($level, $message, $context));
201 26
        }
202 39
    }
203
    
204
205
    /**
206
     * @param  string $level
207
     * @throws InvalidArgumentException
208
     * @return bool
209
     */
210 75
    public function isLogLevel($level) {
211 75
        if (!array_key_exists($level, $this->log_levels)) {
212 6
            throw new InvalidArgumentException('Invalid Log Level');
213
        }
214
        
215 72
        return true;
216
    }
217
218
219
    /**
220
     * @return bool
221
     */
222 39
    public function shouldLog($level) {
223 39
        return $this->log_levels[$level] <= $this->log_levels[$this->level];
224
    }
225
226
227
    /**
228
     * As per PSR-3, messages can be a string, or an object with a __toString() method
229
     *
230
     * @return bool
231
     */
232 45
    public function isStringable($message) {
233 45
        if(gettype($message) == 'string') {
234 39
            return true;
235
        }
236
        
237 6
        if(gettype($message) == 'object' && method_exists($message, '__toString') !== false) {
238 3
            return true;
239
        }
240
        
241 3
        return false;
242
    }
243
244
245
    /**
246
     * @return array
247
     */
248 39
    public function getLog()
249
    {
250 39
        return $this->log;
251
    }
252
253
    /**
254
     * @param  string $level
255
     * @param  string $message
256
     * @param  array  $context
257
     * @return array
258
     */
259 36
    public function defaultFormat($level, $message, array $context = array())
260
    {
261 36
        $logged = array();
262 36
        $logged['timestamp'] = date('Y-m-d H:i:s');
263 36
        $logged['level'] = $level;
264 36
        $logged['message'] = $message;
265 36
        if (!empty($context)) {
266 6
            $logged = array_merge($logged, $this->formatContext($context));
267 4
        }
268
        
269 36
        return $logged;
270
    }
271
    
272
273
    /**
274
     * @param  array  $context
275
     * @return array
276
     */
277 6
    public function formatContext($context) {
278 6
        $parsed = array();
279
        
280
        // Format exception if applicable
281 6
        if ($this->hasException($context)) {
282 3
            $exception = $context['exception'];
283 3
            $parsed['exception'] = sprintf(
284 3
                'Exception %s; message: %s; trace: %s',
285 2
                get_class($exception),
286 3
                $exception->getMessage(),
287 3
                json_encode($exception->getTraceAsString())
288 2
            );
289
            
290 3
            unset($context['exception']);
291 2
        }
292
        
293
        // Format other context if applicable
294 6
        if (count($context)>0){
295 3
            $parsed['context'] = 'Context: ' . json_encode($context);
296 2
        }
297
        
298 6
        return $parsed;
299
    }
300
301
302
    /**
303
     * @param  array  $context
304
     * @return bool
305
     */
306 6
    public function hasException($context) {
307 6
        return ( array_key_exists('exception', $context) === true && $context['exception'] instanceof \Exception === true );
308
    }
309
310
311
    /**
312
     * @param  callable $message_format
313
     * @return void
314
     */
315 72
    public function setMessageFormat($message_format)
316
    {
317 72
        $this->message_format = $message_format;
318 72
    }
319
320
321
    /**
322
     * @return callable
323
     */
324 42
    public function getMessageFormat()
325
    {
326 42
        return $this->message_format;
327
    }
328
329
330
    /**
331
     * @param  string $level
332
     * @return void
333
     */
334 3
    public function setLevel($level)
335
    {
336 3
        $this->level = $level;
337 3
    }
338
339
340
    /**
341
     * @return int
342
     */
343 24
    public function getLevel()
344
    {
345 24
        return $this->level;
346
    }
347
348
}
349