Log::format()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 16
rs 9.9332
1
<?php
2
3
namespace Tleckie\Log;
4
5
use DateTimeImmutable;
6
use Psr\Log\AbstractLogger;
7
use Psr\Log\InvalidArgumentException;
8
use Psr\Log\LoggerInterface;
9
use Tleckie\Log\Formatter\Handler\ArrayHandler;
10
use Tleckie\Log\Formatter\Handler\ExceptionHandler;
11
use Tleckie\Log\Formatter\Handler\HandlerInterface;
12
use Tleckie\Log\Formatter\Handler\NumericHandler;
13
use Tleckie\Log\Formatter\Handler\ObjectHandler;
14
use Tleckie\Log\Formatter\Handler\StringHandler;
15
16
/**
17
 * Class Log
18
 * Default format: [%date%] %channel%.%level% %message% %context%
19
 *
20
 * @package Tleckie\Log
21
 * @author  Teodoro Leckie Westberg <[email protected]>
22
 */
23
class Log extends AbstractLogger
24
{
25
    /** @var array */
26
    protected const LEVELS = [
27
        Level::EMERGENCY,
28
        Level::ALERT,
29
        Level::CRITICAL,
30
        Level::ERROR,
31
        Level::WARNING,
32
        Level::NOTICE,
33
        Level::INFO,
34
        Level::DEBUG
35
    ];
36
37
    /** @var HandlerInterface */
38
    protected HandlerInterface $handlerFormatter;
39
40
    /** @var LoggerInterface[] */
41
    private array $handlers;
42
43
    /** @var string */
44
    private string $channel;
45
46
    /** @var string */
47
    private string $format;
48
49
    /**
50
     * Log constructor.
51
     *
52
     * @param LoggerInterface[] $handlers
53
     * @param string            $channel
54
     * InvalidArgumentException
55
     * @param string            $format
56
     */
57
    public function __construct(
58
        array $handlers,
59
        string $channel = 'channel',
60
        string $format = '[%date%] %channel%.%level% %message% %context%'
61
    ) {
62
        foreach ($handlers as $handler) {
63
            if (!$handler instanceof LoggerInterface) {
64
                throw new InvalidArgumentException("Handlers have to be of type " . LoggerInterface::class);
65
            }
66
        }
67
68
        $this->channel = $channel;
69
        $this->handlers = $handlers;
70
        $this->format = $format;
71
        $this->handlerFormatter();
72
    }
73
74
    private function handlerFormatter(): void
75
    {
76
        $this->handlerFormatter = new NumericHandler();
77
        $this->handlerFormatter
78
            ->next(new ArrayHandler())
79
            ->next(new ExceptionHandler())
80
            ->next(new ObjectHandler())
81
            ->next(new StringHandler());
82
    }
83
84
    /**
85
     * @param mixed  $level
86
     * @param string $message
87
     * @param array  $context
88
     * @throws InvalidArgumentException
89
     */
90
    public function log($level, $message, array $context = array()): void
91
    {
92
        if (!in_array($level, static::LEVELS, true)) {
93
            throw new InvalidArgumentException("Unrecognized [$level] level");
94
        }
95
96
        $stringMessage = $this->handlerFormatter->handler($message, $context);
97
        $formattedMessage = $this->format($level, $stringMessage, $context);
98
99
        foreach ($this->handlers as $handler) {
100
            $handler->log($level, $formattedMessage, $context);
101
        }
102
    }
103
104
    /**
105
     * @param string $level
106
     * @param string $message
107
     * @param array  $context
108
     * @return string
109
     */
110
    private function format(string $level, string $message, array $context): string
111
    {
112
        $replace = [
113
            '%date%' => (new DateTimeImmutable('now'))->format('Y-m-d\TH:i:sP'),
114
            '%channel%' => $this->channel,
115
            '%level%' => strtoupper($level),
116
            '%message%' => $message,
117
            '%context%' => json_encode($context, JSON_UNESCAPED_SLASHES) ?? ''
118
        ];
119
120
        $format = $this->format;
121
        foreach ($replace as $search => $item) {
122
            $format = str_replace($search, $item, $format);
123
        }
124
125
        return $format . "\n";
126
    }
127
}
128