Completed
Push — master ( efa801...d3c12f )
by Mihail
12s queued 11s
created

Log.php (1 issue)

Labels
Severity
1
<?php
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 Psr\Log\LoggerTrait;
17
use Throwable;
18
19
/**
20
 * Class Log for logging different types of application messages.
21
 *
22
 * $log->notice('foo');
23
 * $log->warn('bar');
24
 *
25
 *  CONFIGURATION PARAMETERS (Log class)
26
 *
27
 *      -   deferred (bool)         [optional], default: false
28
 *          A flag to set the Log instance how to dump messages.
29
 *          Set to TRUE if you want to process all accumulated messages
30
 *          at shutdown time. Otherwise, the default behavior is to process
31
 *          the message immediately after the LoggerInterface method is called.
32
 *
33
 *      -   loggers (array)
34
 *          An array of log processors. Every processor is defined in array with it's own
35
 *          configuration parameters, but ALL must have the following:
36
 *
37
 *          -   class       (string)    [required]
38
 *              The name of the log processor class.
39
 *              Can create multiple same instances with different config
40
 *              parameters.
41
 *
42
 *          -   levels      (integer)    [optional], default: -1 (for all levels)
43
 *              Packed integer for bitwise comparison. See the constants in this
44
 *              class.
45
 *
46
 *              Example: Log::INFO | Log::ERROR | Log::ALERT
47
 *              Processor with these log levels will store only
48
 *              info, error and warning type messages.
49
 *
50
 *      -   dateformat  (string)    [optional], default: d/m/Y H:i:s.u
51
 *          The date format for the log message.
52
 *
53
 *      -   timezone    (string)    [optional], default: UTC
54
 *          The desired timezone for the DateTimeZone object.
55
 *
56
 *
57
 *  CONFIGURATION PARAMETERS (Processor class)
58
 *  Every processor may have it's own specific parameters.
59
 *
60
 */
61
class Log implements Logger
62
{
63
    use LoggerTrait;
64
65
    /**
66
     * @var bool Flag to control the messages processing
67
     */
68
    private $deferred = false;
69
70
    /**
71
     * @var string The date format for the message.
72
     */
73
    private $dateFormat;
74
75
    /**
76
     * @var string Valid timezone for the message.
77
     */
78
    private $timezone = 'UTC';
79
80
    /**
81
     * @var Processor[] Hash with all registered log processors.
82
     */
83
    private $processors = [];
84
85
    /**
86
     * @var array List with all accumulated messages.
87
     */
88
    private $messages = [];
89
90
    /**
91
     * Creates all requested log processors.
92
     *
93
     * @param array $settings
94
     */
95 9
    public function __construct(array $settings)
96
    {
97 9
        $this->deferred = (bool)($settings['deferred'] ?? false);
98 9
        $this->dateFormat = (string)($settings['dateformat'] ?? 'd/m/Y H:i:s.u');
99 9
        $this->timezone = (string)($settings['timezone'] ?? $this->timezone);
100
101 9
        foreach ((array)($settings['loggers'] ?? []) as $processor) {
102 1
            $this->attach(new $processor['class']($processor));
103
        }
104
105 9
        if ($this->deferred) {
106 3
            register_shutdown_function([$this, 'process']);
107
        }
108 9
    }
109
110 3
    public function attach(Processor $processor): Logger
111
    {
112 3
        if (0 !== $processor->levels()) {
113 3
            $this->processors[spl_object_hash($processor)] = $processor;
114
        }
115
116 3
        return $this;
117
    }
118
119 3
    public function log($level, $message, array $context = [])
120
    {
121
        try {
122 3
            $levelname = strtoupper($level);
123 3
            $level = constant('self::' . $levelname);
124 1
        } catch (Throwable $e) {
125 1
            $levelname = 'LOG';
126 1
            $level = -1;
127
        }
128
129 3
        $this->messages[] = [
130 3
            'level'     => $level,
131 3
            'levelname' => $levelname,
132 3
            'message'   => $this->formatMessage($message, $context),
133 3
            'timestamp' => date_create_immutable('now', timezone_open($this->timezone))->format($this->dateFormat),
0 ignored issues
show
It seems like timezone_open($this->timezone) can also be of type false; however, parameter $timezone of date_create_immutable() does only seem to accept DateTimeZone|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

133
            'timestamp' => date_create_immutable('now', /** @scrutinizer ignore-type */ timezone_open($this->timezone))->format($this->dateFormat),
Loading history...
134
        ];
135
136 3
        $this->deferred || $this->process();
137 3
    }
138
139
    /**
140
     * Parses the message as in the interface specification.
141
     *
142
     * @param string|object $message A string or object that implements __toString
143
     * @param array         $params  [optional] Arbitrary data with key-value pairs replacements
144
     *
145
     * @return string
146
     */
147 3
    private function formatMessage($message, array $params = []): string
148
    {
149 3
        $replacements = [];
150 3
        foreach ($params as $k => $v) {
151 1
            $replacements['{' . $k . '}'] = $v;
152
        }
153
154 3
        return strtr((string)$message, $replacements);
155
    }
156
157 1
    public function process(): void
158
    {
159 1
        foreach ($this->processors as $processor) {
160 1
            $processor->update($this->messages);
161
        }
162
163 1
        $this->messages = [];
164 1
    }
165
166 1
    public function exception(Throwable $e, Processor $processor = null): void
167
    {
168 1
        $logger = $processor ?? new Cli([]);
169 1
        $message = $e->getMessage() . PHP_EOL . ' -- [Trace]: ' . $e->getTraceAsString();
170
171 1
        $this->attach($logger)->critical($message);
172 1
        $this->process();
173 1
        $this->detach($logger);
174 1
    }
175
176 2
    public function detach(Processor $processor): Logger
177
    {
178 2
        unset($this->processors[spl_object_hash($processor)]);
179
180 2
        return $this;
181
    }
182
}
183