Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
103 - 1
<?php
2
 
3
/*
4
 * This file is part of the Monolog package.
5
 *
6
 * (c) Jordi Boggiano <j.boggiano@seld.be>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Monolog;
13
 
14
use Monolog\Handler\HandlerInterface;
15
use Monolog\Handler\StreamHandler;
16
use Psr\Log\LoggerInterface;
17
use Psr\Log\InvalidArgumentException;
18
 
19
/**
20
 * Monolog log channel
21
 *
22
 * It contains a stack of Handlers and a stack of Processors,
23
 * and uses them to store records that are added to it.
24
 *
25
 * @author Jordi Boggiano <j.boggiano@seld.be>
26
 */
27
class Logger implements LoggerInterface
28
{
29
    /**
30
     * Detailed debug information
31
     */
32
    const DEBUG = 100;
33
 
34
    /**
35
     * Interesting events
36
     *
37
     * Examples: User logs in, SQL logs.
38
     */
39
    const INFO = 200;
40
 
41
    /**
42
     * Uncommon events
43
     */
44
    const NOTICE = 250;
45
 
46
    /**
47
     * Exceptional occurrences that are not errors
48
     *
49
     * Examples: Use of deprecated APIs, poor use of an API,
50
     * undesirable things that are not necessarily wrong.
51
     */
52
    const WARNING = 300;
53
 
54
    /**
55
     * Runtime errors
56
     */
57
    const ERROR = 400;
58
 
59
    /**
60
     * Critical conditions
61
     *
62
     * Example: Application component unavailable, unexpected exception.
63
     */
64
    const CRITICAL = 500;
65
 
66
    /**
67
     * Action must be taken immediately
68
     *
69
     * Example: Entire website down, database unavailable, etc.
70
     * This should trigger the SMS alerts and wake you up.
71
     */
72
    const ALERT = 550;
73
 
74
    /**
75
     * Urgent alert.
76
     */
77
    const EMERGENCY = 600;
78
 
79
    /**
80
     * Monolog API version
81
     *
82
     * This is only bumped when API breaks are done and should
83
     * follow the major version of the library
84
     *
85
     * @var int
86
     */
87
    const API = 1;
88
 
89
    /**
90
     * Logging levels from syslog protocol defined in RFC 5424
91
     *
92
     * @var array $levels Logging levels
93
     */
94
    protected static $levels = array(
95
        self::DEBUG     => 'DEBUG',
96
        self::INFO      => 'INFO',
97
        self::NOTICE    => 'NOTICE',
98
        self::WARNING   => 'WARNING',
99
        self::ERROR     => 'ERROR',
100
        self::CRITICAL  => 'CRITICAL',
101
        self::ALERT     => 'ALERT',
102
        self::EMERGENCY => 'EMERGENCY',
103
    );
104
 
105
    /**
106
     * @var \DateTimeZone
107
     */
108
    protected static $timezone;
109
 
110
    /**
111
     * @var string
112
     */
113
    protected $name;
114
 
115
    /**
116
     * The handler stack
117
     *
118
     * @var HandlerInterface[]
119
     */
120
    protected $handlers;
121
 
122
    /**
123
     * Processors that will process all log records
124
     *
125
     * To process records of a single handler instead, add the processor on that specific handler
126
     *
127
     * @var callable[]
128
     */
129
    protected $processors;
130
 
131
    /**
132
     * @var bool
133
     */
134
    protected $microsecondTimestamps = true;
135
 
136
    /**
137
     * @param string             $name       The logging channel
138
     * @param HandlerInterface[] $handlers   Optional stack of handlers, the first one in the array is called first, etc.
139
     * @param callable[]         $processors Optional array of processors
140
     */
141
    public function __construct($name, array $handlers = array(), array $processors = array())
142
    {
143
        $this->name = $name;
144
        $this->handlers = $handlers;
145
        $this->processors = $processors;
146
    }
147
 
148
    /**
149
     * @return string
150
     */
151
    public function getName()
152
    {
153
        return $this->name;
154
    }
155
 
156
    /**
157
     * Return a new cloned instance with the name changed
158
     *
159
     * @return static
160
     */
161
    public function withName($name)
162
    {
163
        $new = clone $this;
164
        $new->name = $name;
165
 
166
        return $new;
167
    }
168
 
169
    /**
170
     * Pushes a handler on to the stack.
171
     *
172
     * @param  HandlerInterface $handler
173
     * @return $this
174
     */
175
    public function pushHandler(HandlerInterface $handler)
176
    {
177
        array_unshift($this->handlers, $handler);
178
 
179
        return $this;
180
    }
181
 
182
    /**
183
     * Pops a handler from the stack
184
     *
185
     * @return HandlerInterface
186
     */
187
    public function popHandler()
188
    {
189
        if (!$this->handlers) {
190
            throw new \LogicException('You tried to pop from an empty handler stack.');
191
        }
192
 
193
        return array_shift($this->handlers);
194
    }
195
 
196
    /**
197
     * Set handlers, replacing all existing ones.
198
     *
199
     * If a map is passed, keys will be ignored.
200
     *
201
     * @param  HandlerInterface[] $handlers
202
     * @return $this
203
     */
204
    public function setHandlers(array $handlers)
205
    {
206
        $this->handlers = array();
207
        foreach (array_reverse($handlers) as $handler) {
208
            $this->pushHandler($handler);
209
        }
210
 
211
        return $this;
212
    }
213
 
214
    /**
215
     * @return HandlerInterface[]
216
     */
217
    public function getHandlers()
218
    {
219
        return $this->handlers;
220
    }
221
 
222
    /**
223
     * Adds a processor on to the stack.
224
     *
225
     * @param  callable $callback
226
     * @return $this
227
     */
228
    public function pushProcessor($callback)
229
    {
230
        if (!is_callable($callback)) {
231
            throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
232
        }
233
        array_unshift($this->processors, $callback);
234
 
235
        return $this;
236
    }
237
 
238
    /**
239
     * Removes the processor on top of the stack and returns it.
240
     *
241
     * @return callable
242
     */
243
    public function popProcessor()
244
    {
245
        if (!$this->processors) {
246
            throw new \LogicException('You tried to pop from an empty processor stack.');
247
        }
248
 
249
        return array_shift($this->processors);
250
    }
251
 
252
    /**
253
     * @return callable[]
254
     */
255
    public function getProcessors()
256
    {
257
        return $this->processors;
258
    }
259
 
260
    /**
261
     * Control the use of microsecond resolution timestamps in the 'datetime'
262
     * member of new records.
263
     *
264
     * Generating microsecond resolution timestamps by calling
265
     * microtime(true), formatting the result via sprintf() and then parsing
266
     * the resulting string via \DateTime::createFromFormat() can incur
267
     * a measurable runtime overhead vs simple usage of DateTime to capture
268
     * a second resolution timestamp in systems which generate a large number
269
     * of log events.
270
     *
271
     * @param bool $micro True to use microtime() to create timestamps
272
     */
273
    public function useMicrosecondTimestamps($micro)
274
    {
275
        $this->microsecondTimestamps = (bool) $micro;
276
    }
277
 
278
    /**
279
     * Adds a log record.
280
     *
281
     * @param  int     $level   The logging level
282
     * @param  string  $message The log message
283
     * @param  array   $context The log context
284
     * @return Boolean Whether the record has been processed
285
     */
286
    public function addRecord($level, $message, array $context = array())
287
    {
288
        if (!$this->handlers) {
289
            $this->pushHandler(new StreamHandler('php://stderr', static::DEBUG));
290
        }
291
 
292
        $levelName = static::getLevelName($level);
293
 
294
        // check if any handler will handle this message so we can return early and save cycles
295
        $handlerKey = null;
296
        reset($this->handlers);
297
        while ($handler = current($this->handlers)) {
298
            if ($handler->isHandling(array('level' => $level))) {
299
                $handlerKey = key($this->handlers);
300
                break;
301
            }
302
 
303
            next($this->handlers);
304
        }
305
 
306
        if (null === $handlerKey) {
307
            return false;
308
        }
309
 
310
        if (!static::$timezone) {
311
            static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC');
312
        }
313
 
314
        // php7.1+ always has microseconds enabled, so we do not need this hack
315
        if ($this->microsecondTimestamps && PHP_VERSION_ID < 70100) {
316
            $ts = \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone);
317
        } else {
318
            $ts = new \DateTime(null, static::$timezone);
319
        }
320
        $ts->setTimezone(static::$timezone);
321
 
322
        $record = array(
323
            'message' => (string) $message,
324
            'context' => $context,
325
            'level' => $level,
326
            'level_name' => $levelName,
327
            'channel' => $this->name,
328
            'datetime' => $ts,
329
            'extra' => array(),
330
        );
331
 
332
        foreach ($this->processors as $processor) {
333
            $record = call_user_func($processor, $record);
334
        }
335
 
336
        while ($handler = current($this->handlers)) {
337
            if (true === $handler->handle($record)) {
338
                break;
339
            }
340
 
341
            next($this->handlers);
342
        }
343
 
344
        return true;
345
    }
346
 
347
    /**
348
     * Adds a log record at the DEBUG level.
349
     *
350
     * @param  string  $message The log message
351
     * @param  array   $context The log context
352
     * @return Boolean Whether the record has been processed
353
     */
354
    public function addDebug($message, array $context = array())
355
    {
356
        return $this->addRecord(static::DEBUG, $message, $context);
357
    }
358
 
359
    /**
360
     * Adds a log record at the INFO level.
361
     *
362
     * @param  string  $message The log message
363
     * @param  array   $context The log context
364
     * @return Boolean Whether the record has been processed
365
     */
366
    public function addInfo($message, array $context = array())
367
    {
368
        return $this->addRecord(static::INFO, $message, $context);
369
    }
370
 
371
    /**
372
     * Adds a log record at the NOTICE level.
373
     *
374
     * @param  string  $message The log message
375
     * @param  array   $context The log context
376
     * @return Boolean Whether the record has been processed
377
     */
378
    public function addNotice($message, array $context = array())
379
    {
380
        return $this->addRecord(static::NOTICE, $message, $context);
381
    }
382
 
383
    /**
384
     * Adds a log record at the WARNING level.
385
     *
386
     * @param  string  $message The log message
387
     * @param  array   $context The log context
388
     * @return Boolean Whether the record has been processed
389
     */
390
    public function addWarning($message, array $context = array())
391
    {
392
        return $this->addRecord(static::WARNING, $message, $context);
393
    }
394
 
395
    /**
396
     * Adds a log record at the ERROR level.
397
     *
398
     * @param  string  $message The log message
399
     * @param  array   $context The log context
400
     * @return Boolean Whether the record has been processed
401
     */
402
    public function addError($message, array $context = array())
403
    {
404
        return $this->addRecord(static::ERROR, $message, $context);
405
    }
406
 
407
    /**
408
     * Adds a log record at the CRITICAL level.
409
     *
410
     * @param  string  $message The log message
411
     * @param  array   $context The log context
412
     * @return Boolean Whether the record has been processed
413
     */
414
    public function addCritical($message, array $context = array())
415
    {
416
        return $this->addRecord(static::CRITICAL, $message, $context);
417
    }
418
 
419
    /**
420
     * Adds a log record at the ALERT level.
421
     *
422
     * @param  string  $message The log message
423
     * @param  array   $context The log context
424
     * @return Boolean Whether the record has been processed
425
     */
426
    public function addAlert($message, array $context = array())
427
    {
428
        return $this->addRecord(static::ALERT, $message, $context);
429
    }
430
 
431
    /**
432
     * Adds a log record at the EMERGENCY level.
433
     *
434
     * @param  string  $message The log message
435
     * @param  array   $context The log context
436
     * @return Boolean Whether the record has been processed
437
     */
438
    public function addEmergency($message, array $context = array())
439
    {
440
        return $this->addRecord(static::EMERGENCY, $message, $context);
441
    }
442
 
443
    /**
444
     * Gets all supported logging levels.
445
     *
446
     * @return array Assoc array with human-readable level names => level codes.
447
     */
448
    public static function getLevels()
449
    {
450
        return array_flip(static::$levels);
451
    }
452
 
453
    /**
454
     * Gets the name of the logging level.
455
     *
456
     * @param  int    $level
457
     * @return string
458
     */
459
    public static function getLevelName($level)
460
    {
461
        if (!isset(static::$levels[$level])) {
462
            throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
463
        }
464
 
465
        return static::$levels[$level];
466
    }
467
 
468
    /**
469
     * Converts PSR-3 levels to Monolog ones if necessary
470
     *
471
     * @param string|int Level number (monolog) or name (PSR-3)
472
     * @return int
473
     */
474
    public static function toMonologLevel($level)
475
    {
476
        if (is_string($level) && defined(__CLASS__.'::'.strtoupper($level))) {
477
            return constant(__CLASS__.'::'.strtoupper($level));
478
        }
479
 
480
        return $level;
481
    }
482
 
483
    /**
484
     * Checks whether the Logger has a handler that listens on the given level
485
     *
486
     * @param  int     $level
487
     * @return Boolean
488
     */
489
    public function isHandling($level)
490
    {
491
        $record = array(
492
            'level' => $level,
493
        );
494
 
495
        foreach ($this->handlers as $handler) {
496
            if ($handler->isHandling($record)) {
497
                return true;
498
            }
499
        }
500
 
501
        return false;
502
    }
503
 
504
    /**
505
     * Adds a log record at an arbitrary level.
506
     *
507
     * This method allows for compatibility with common interfaces.
508
     *
509
     * @param  mixed   $level   The log level
510
     * @param  string  $message The log message
511
     * @param  array   $context The log context
512
     * @return Boolean Whether the record has been processed
513
     */
514
    public function log($level, $message, array $context = array())
515
    {
516
        $level = static::toMonologLevel($level);
517
 
518
        return $this->addRecord($level, $message, $context);
519
    }
520
 
521
    /**
522
     * Adds a log record at the DEBUG level.
523
     *
524
     * This method allows for compatibility with common interfaces.
525
     *
526
     * @param  string  $message The log message
527
     * @param  array   $context The log context
528
     * @return Boolean Whether the record has been processed
529
     */
530
    public function debug($message, array $context = array())
531
    {
532
        return $this->addRecord(static::DEBUG, $message, $context);
533
    }
534
 
535
    /**
536
     * Adds a log record at the INFO level.
537
     *
538
     * This method allows for compatibility with common interfaces.
539
     *
540
     * @param  string  $message The log message
541
     * @param  array   $context The log context
542
     * @return Boolean Whether the record has been processed
543
     */
544
    public function info($message, array $context = array())
545
    {
546
        return $this->addRecord(static::INFO, $message, $context);
547
    }
548
 
549
    /**
550
     * Adds a log record at the NOTICE level.
551
     *
552
     * This method allows for compatibility with common interfaces.
553
     *
554
     * @param  string  $message The log message
555
     * @param  array   $context The log context
556
     * @return Boolean Whether the record has been processed
557
     */
558
    public function notice($message, array $context = array())
559
    {
560
        return $this->addRecord(static::NOTICE, $message, $context);
561
    }
562
 
563
    /**
564
     * Adds a log record at the WARNING level.
565
     *
566
     * This method allows for compatibility with common interfaces.
567
     *
568
     * @param  string  $message The log message
569
     * @param  array   $context The log context
570
     * @return Boolean Whether the record has been processed
571
     */
572
    public function warn($message, array $context = array())
573
    {
574
        return $this->addRecord(static::WARNING, $message, $context);
575
    }
576
 
577
    /**
578
     * Adds a log record at the WARNING level.
579
     *
580
     * This method allows for compatibility with common interfaces.
581
     *
582
     * @param  string  $message The log message
583
     * @param  array   $context The log context
584
     * @return Boolean Whether the record has been processed
585
     */
586
    public function warning($message, array $context = array())
587
    {
588
        return $this->addRecord(static::WARNING, $message, $context);
589
    }
590
 
591
    /**
592
     * Adds a log record at the ERROR level.
593
     *
594
     * This method allows for compatibility with common interfaces.
595
     *
596
     * @param  string  $message The log message
597
     * @param  array   $context The log context
598
     * @return Boolean Whether the record has been processed
599
     */
600
    public function err($message, array $context = array())
601
    {
602
        return $this->addRecord(static::ERROR, $message, $context);
603
    }
604
 
605
    /**
606
     * Adds a log record at the ERROR level.
607
     *
608
     * This method allows for compatibility with common interfaces.
609
     *
610
     * @param  string  $message The log message
611
     * @param  array   $context The log context
612
     * @return Boolean Whether the record has been processed
613
     */
614
    public function error($message, array $context = array())
615
    {
616
        return $this->addRecord(static::ERROR, $message, $context);
617
    }
618
 
619
    /**
620
     * Adds a log record at the CRITICAL level.
621
     *
622
     * This method allows for compatibility with common interfaces.
623
     *
624
     * @param  string  $message The log message
625
     * @param  array   $context The log context
626
     * @return Boolean Whether the record has been processed
627
     */
628
    public function crit($message, array $context = array())
629
    {
630
        return $this->addRecord(static::CRITICAL, $message, $context);
631
    }
632
 
633
    /**
634
     * Adds a log record at the CRITICAL level.
635
     *
636
     * This method allows for compatibility with common interfaces.
637
     *
638
     * @param  string  $message The log message
639
     * @param  array   $context The log context
640
     * @return Boolean Whether the record has been processed
641
     */
642
    public function critical($message, array $context = array())
643
    {
644
        return $this->addRecord(static::CRITICAL, $message, $context);
645
    }
646
 
647
    /**
648
     * Adds a log record at the ALERT level.
649
     *
650
     * This method allows for compatibility with common interfaces.
651
     *
652
     * @param  string  $message The log message
653
     * @param  array   $context The log context
654
     * @return Boolean Whether the record has been processed
655
     */
656
    public function alert($message, array $context = array())
657
    {
658
        return $this->addRecord(static::ALERT, $message, $context);
659
    }
660
 
661
    /**
662
     * Adds a log record at the EMERGENCY level.
663
     *
664
     * This method allows for compatibility with common interfaces.
665
     *
666
     * @param  string  $message The log message
667
     * @param  array   $context The log context
668
     * @return Boolean Whether the record has been processed
669
     */
670
    public function emerg($message, array $context = array())
671
    {
672
        return $this->addRecord(static::EMERGENCY, $message, $context);
673
    }
674
 
675
    /**
676
     * Adds a log record at the EMERGENCY level.
677
     *
678
     * This method allows for compatibility with common interfaces.
679
     *
680
     * @param  string  $message The log message
681
     * @param  array   $context The log context
682
     * @return Boolean Whether the record has been processed
683
     */
684
    public function emergency($message, array $context = array())
685
    {
686
        return $this->addRecord(static::EMERGENCY, $message, $context);
687
    }
688
 
689
    /**
690
     * Set the timezone to be used for the timestamp of log records.
691
     *
692
     * This is stored globally for all Logger instances
693
     *
694
     * @param \DateTimeZone $tz Timezone object
695
     */
696
    public static function setTimezone(\DateTimeZone $tz)
697
    {
698
        self::$timezone = $tz;
699
    }
700
}