Passed
Push — master ( 0541b3...deb180 )
by Mihail
18:22 queued 09:14
created

Log::now()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
ccs 1
cts 1
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of the Koded package.
5
 *
6
 * (c) Mihail Binev <[email protected]>
7
 *
8
 * Please view the LICENSE distributed with this source code
9
 * for the full copyright and license information.
10
 *
11
 */
12
13
namespace Koded\Logging;
14
15
use Koded\Logging\Processors\{Cli, Processor};
16
use DateTimeZone;
17
use Psr\Log\LoggerTrait;
18
use Throwable;
19
20
/**
21
 * Class Log for logging different types of application messages.
22
 *
23
 * $log->notice('foo');
24
 * $log->warn('bar');
25
 *
26
 *  CONFIGURATION PARAMETERS (Log class)
27
 *
28
 *      -   processors (array)
29
 *          An array of log processors. Every processor is defined in array with it's own
30
 *          configuration parameters, but ALL must have the following:
31
 *
32
 *          -   class       (string)    [required]
33
 *              The name of the log processor class.
34
 *              Can create multiple same instances with different config
35
 *              parameters.
36
 *
37
 *          -   levels      (integer)    [optional], default: -1 (for all levels)
38
 *              Packed integer for bitwise comparison. See the constants in this
39
 *              class.
40
 *
41
 *              Example: Log::INFO | Log::ERROR | Log::ALERT
42
 *              Processor with these log levels will store only
43
 *              info, error and warning type messages.
44
 *
45
 *      -   dateformat  (string)    [optional], default: d/m/Y H:i:s.u
46
 *          The date format for the log message.
47
 *
48
 *      -   timezone    (string)    [optional], default: UTC
49
 *          The desired timezone for the DateTimeZone object.
50
 *
51
 *
52
 *  CONFIGURATION PARAMETERS (Processor class)
53
 *  Every processor may have its own specific parameters.
54
 *
55
 */
56
class Log implements Logger
57
{
58
    use LoggerTrait;
59
60
    private const DATE_FORMAT = 'd/m/Y H:i:s.u';
61
62
    private DateTimeZone $timezone;
63
64
    /**
65
     * @var array<int, Processor> Hash with all registered log processors.
66
     */
67
    private array $processors = [];
68
69
    /**
70
     * Creates all requested log processors.
71
     *
72
     * @param array  $processors a list of log processors
73
     * @param string $dateformat The date format for the messages
74
     * @param string $timezone   The timezone for the messages
75
     */
76
    public function __construct(
77
        array $processors,
78
        private string $dateformat = self::DATE_FORMAT,
79
        string $timezone = 'UTC')
80
    {
81
        $this->timezone = @\timezone_open($timezone) ?: \timezone_open('UTC');
82
        foreach ($processors as $processor) {
83
            $this->attach(new $processor['class']($processor));
84
        }
85
    }
86
87
    public function log($level, $message, array $context = []): void
88
    {
89
        try {
90
            $levelName = \strtoupper($level);
91
            $level = \constant('static::' . $levelName);
92
        } catch (Throwable) {
93
            $levelName = 'LOG';
94
            $level = -1;
95 9
        }
96
        foreach ($this->processors as $processor) {
97 9
            $processor->update([
98 9
               'level' => $level,
99 9
               'levelname' => $levelName,
100
               'message' => $this->formatMessage($message, $context),
101 9
               'timestamp' => $this->now(),
102 1
           ]);
103
        }
104
    }
105 9
106 3
    public function attach(Processor $processor): Logger
107
    {
108 9
        if (0 !== $processor->levels()) {
109
            $this->processors[\spl_object_id($processor)] = $processor;
110 3
        }
111
        return $this;
112 3
    }
113 3
114
    public function detach(Processor $processor): Logger
115
    {
116 3
        unset($this->processors[\spl_object_id($processor)]);
117
        return $this;
118
    }
119 3
120
    public function exception(Throwable $e, Processor $processor = null): void
121
    {
122 3
        $this->attach($processor ??= new Cli([]));
123 3
        $this->critical($e->getMessage() . PHP_EOL . ' -- [Trace]: ' . $e->getTraceAsString());
124 1
        $this->detach($processor);
125 1
    }
126 1
127
    /**
128
     * Parses the message as in the interface specification.
129 3
     *
130 3
     * @param object|string $message A string or object that implements __toString
131 3
     * @param array         $params  [optional] Arbitrary data with key-value pairs replacements
132 3
     *
133 3
     * @return string
134
     */
135
    private function formatMessage(object|string $message, array $params = []): string
136 3
    {
137 3
        $replacements = [];
138
        foreach ($params as $k => $v) {
139
            $replacements['{' . $k . '}'] = $v;
140
        }
141
        return \strtr((string)$message, $replacements);
142
    }
143
144
    private function now(): string
145
    {
146
        return \date_create_immutable('now', $this->timezone)
147 3
            ->format($this->dateformat);
148
    }
149
}
150