Issues (536)

src/Debug/Logger.php (10 issues)

1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Debug;
13
14
use InvalidArgumentException;
15
use Monolog\Formatter\HtmlFormatter;
16
use Monolog\Formatter\JsonFormatter;
17
use Monolog\Formatter\LineFormatter;
18
use Monolog\Formatter\NormalizerFormatter;
19
use Monolog\Formatter\ScalarFormatter;
20
use Monolog\Handler\BrowserConsoleHandler;
21
use Monolog\Handler\ChromePHPHandler;
22
use Monolog\Handler\ErrorLogHandler;
23
use Monolog\Handler\FirePHPHandler;
24
use Monolog\Handler\NativeMailerHandler;
25
use Monolog\Handler\RotatingFileHandler;
26
use Monolog\Handler\StreamHandler;
27
use Monolog\Handler\TelegramBotHandler;
28
use Monolog\Logger as MonologLogger;
29
use Monolog\Processor\HostnameProcessor;
30
use Monolog\Processor\IntrospectionProcessor;
31
use Monolog\Processor\MemoryUsageProcessor;
32
use Monolog\Processor\ProcessIdProcessor;
33
use Monolog\Processor\PsrLogMessageProcessor;
34
use Monolog\Processor\UidProcessor;
35
use Monolog\Processor\WebProcessor;
36
use Psr\Log\LoggerInterface;
37
use Psr\Log\LogLevel;
38
use stdClass;
39
use Stringable;
40
41
class Logger implements LoggerInterface
42
{
43
    /**
44
     * Options de configuration provenant de app/Config/log.php
45
     *
46
     * @var object
47
     */
48
    private readonly stdClass $config;
49
50
    /**
51
     * Met en cache les appels de journalisation pour la barre de débogage.
52
     */
53
    public array $logCache = [];
54
55
    /**
56
     * Devrions-nous mettre en cache nos éléments enregistrés ?
57
     *
58
     * @var bool
59
     */
60
    protected $cacheLogs = false;
61
62
    /**
63
     * Instance monolog
64
     */
65
    private readonly MonologLogger $monolog;
66
67
    public function __construct(bool $debug = BLITZ_DEBUG)
68
    {
69
        $this->config = (object) config('log');
0 ignored issues
show
The property config is declared read-only in BlitzPHP\Debug\Logger.
Loading history...
70
71
        $this->monolog = new MonologLogger($this->config->name ?? 'application');
0 ignored issues
show
The property monolog is declared read-only in BlitzPHP\Debug\Logger.
Loading history...
72
73
        foreach (($this->config->handlers ?? []) as $handler => $options) {
74
            $this->pushHandler($handler, (object) $options);
75
        }
76
77
        foreach (($this->config->processors ?? []) as $processor) {
78
            $this->pushProcessor($processor);
79
        }
80
81
        $this->cacheLogs = $debug;
82
        if ($this->cacheLogs) {
83
            $this->logCache = [];
84
        }
85
    }
86
87
    /**
88
     * {@inheritDoc}
89
     */
90
    public function emergency(string|Stringable $message, array $context = []): void
91
    {
92
        $this->log(LogLevel::EMERGENCY, $message, $context);
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     */
98
    public function alert(string|Stringable $message, array $context = []): void
99
    {
100
        $this->log(LogLevel::ALERT, $message, $context);
101
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106
    public function critical(string|Stringable $message, array $context = []): void
107
    {
108
        $this->log(LogLevel::CRITICAL, $message, $context);
109
    }
110
111
    /**
112
     * {@inheritDoc}
113
     */
114
    public function error(string|Stringable $message, array $context = []): void
115
    {
116
        $this->log(LogLevel::ERROR, $message, $context);
117
    }
118
119
    /**
120
     * {@inheritDoc}
121
     */
122
    public function warning(string|Stringable $message, array $context = []): void
123
    {
124
        $this->log(LogLevel::WARNING, $message, $context);
125
    }
126
127
    /**
128
     * {@inheritDoc}
129
     */
130
    public function notice(string|Stringable $message, array $context = []): void
131
    {
132
        $this->log(LogLevel::NOTICE, $message, $context);
133
    }
134
135
    /**
136
     * {@inheritDoc}
137
     */
138
    public function info(string|Stringable $message, array $context = []): void
139
    {
140
        $this->log(LogLevel::INFO, $message, $context);
141
    }
142
143
    /**
144
     * {@inheritDoc}
145
     */
146
    public function debug(string|Stringable $message, array $context = []): void
147
    {
148 21
        $this->log(LogLevel::DEBUG, $message, $context);
149
    }
150
151
    /**
152
     * {@inheritDoc}
153
     */
154
    public function log($level, string|Stringable $message, array $context = []): void
155
    {
156 21
        $this->monolog->log($level, $message, $context);
157
158
        if ($this->cacheLogs) {
159
            $this->logCache[] = [
160
                'level' => $level,
161
                'msg'   => $message,
162 21
            ];
163
        }
164
    }
165
166
    /**
167
     * Ajoute les differents gestionnaires prise en charge par la configuration /app/Config/log.php
168
     */
169
    private function pushHandler(string $handler, stdClass $options)
170
    {
171
        match ($handler) {
172
            'error'    => $this->pushErrorHandler($options),
0 ignored issues
show
Are you sure the usage of $this->pushErrorHandler($options) targeting BlitzPHP\Debug\Logger::pushErrorHandler() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
173
            'email'    => $this->pushEmailHandler($options),
0 ignored issues
show
Are you sure the usage of $this->pushEmailHandler($options) targeting BlitzPHP\Debug\Logger::pushEmailHandler() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
174
            'telegram' => $this->pushTelegramHandler($options),
0 ignored issues
show
Are you sure the usage of $this->pushTelegramHandler($options) targeting BlitzPHP\Debug\Logger::pushTelegramHandler() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
175
            'chrome'   => $this->pushChromeHandler($options),
0 ignored issues
show
Are you sure the usage of $this->pushChromeHandler($options) targeting BlitzPHP\Debug\Logger::pushChromeHandler() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
176
            'firebug'  => $this->pushFirebugHandler($options),
0 ignored issues
show
Are you sure the usage of $this->pushFirebugHandler($options) targeting BlitzPHP\Debug\Logger::pushFirebugHandler() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
177
            'browser'  => $this->pushBrowserHandler($options),
0 ignored issues
show
Are you sure the usage of $this->pushBrowserHandler($options) targeting BlitzPHP\Debug\Logger::pushBrowserHandler() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
178
            default    => $this->pushFileHandler($options),
0 ignored issues
show
Are you sure the usage of $this->pushFileHandler($options) targeting BlitzPHP\Debug\Logger::pushFileHandler() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
179
        };
180
    }
181
182
    /**
183
     * Ajoute un gestionnaire de log de type Fichier
184
     *
185
     * Enregistre les problèmes dans des fichiers de journalisation
186
     */
187
    private function pushFileHandler(stdClass $options): void
188
    {
189
        $directory = rtrim($options->path ?: LOG_PATH, DS) . DS;
190
        $filename  = strtolower($this->config->name ?: 'application');
191
        $extension = $options->extension ?: '.log';
192
193
        if (($options->dayly_rotation ?: true) === true) {
194
            $handler = new RotatingFileHandler($directory . $filename . $extension, $options->max_files ?: 0, $options->level ?: LogLevel::DEBUG, true, $options->permissions ?: 644);
195
        } else {
196
            $handler = new StreamHandler($directory . $filename . $extension, $options->level ?: LogLevel::DEBUG, true, $options->permissions ?: 644);
197
        }
198
199
        $this->monolog->pushHandler(
200
            $this->setFormatter($handler, ['json', 'line', 'scalar', 'normalizer'], $options->format)
201
        );
202
    }
203
204
    /**
205
     * Ajoute un gestionnaire de log de type PHP error_log
206
     *
207
     * Enregistre les problèmes dans la fonction PHP error_log().
208
     */
209
    private function pushErrorHandler(stdClass $options): void
210
    {
211
        $handler = new ErrorLogHandler($options->type ?: ErrorLogHandler::OPERATING_SYSTEM, $options->level ?: LogLevel::DEBUG);
212
213
        $this->monolog->pushHandler(
214
            $this->setFormatter($handler, ['json', 'line'], $options->format)
215
        );
216
    }
217
218
    /**
219
     * Ajoute un gestionnaire de log de type Email
220
     *
221
     * Envoi un email à l'administrateur en cas de problème
222
     */
223
    private function pushEmailHandler(stdClass $options): void
224
    {
225
        $handler = new NativeMailerHandler($options->to, $options->subject, $options->from, $options->level ?: LogLevel::ERROR);
226
227
        $this->monolog->pushHandler(
228
            $this->setFormatter($handler, ['html', 'json', 'line'], $options->format)
229
        );
230
    }
231
232
    private function pushTelegramHandler(stdClass $options): void
233
    {
234
        $handler = new TelegramBotHandler($options->api_key, $options->channel, $options->level ?: LogLevel::DEBUG);
235
236
        $this->monolog->pushHandler(
237
            $this->setFormatter($handler, [], $options->format)
238
        );
239
    }
240
241
    /**
242
     * Ajoute un gestionnaire de log pour chrome
243
     *
244
     * Affichera les log dans la console de chrome
245
     */
246
    private function pushChromeHandler(stdClass $options): void
247
    {
248
        $handler = new ChromePHPHandler($options->level ?: LogLevel::DEBUG);
249
250
        $this->monolog->pushHandler(
251
            $this->setFormatter($handler, [], $options->format)
252
        );
253
    }
254
255
    /**
256
     * Ajoute un gestionnaire de log pour firebug
257
     *
258
     * Affichera les log dans la console firebug
259
     */
260
    private function pushFirebugHandler(stdClass $options): void
261
    {
262
        $handler = new FirePHPHandler();
263
264
        $this->monolog->pushHandler(
265
            $this->setFormatter($handler, [], $options->format)
266
        );
267
    }
268
269
    /**
270
     * Ajoute un gestionnaire de log pour les navigateurs
271
     *
272
     * Affichera les log dans la console des navigateurs
273
     */
274
    private function pushBrowserHandler(stdClass $options): void
275
    {
276
        $handler = new BrowserConsoleHandler();
277
278
        $this->monolog->pushHandler(
279
            $this->setFormatter($handler, [], $options->format)
280
        );
281
    }
282
283
    /**
284
     * Ajoute les processeur au gestionnaire de log
285
     *
286
     * Un processor permet d'ajouter des méta données aux log générés
287
     */
288
    private function pushProcessor(string $processor)
289
    {
290
        match ($processor) {
291
            'web'           => $this->monolog->pushProcessor(new WebProcessor()),
292
            'introspection' => $this->monolog->pushProcessor(new IntrospectionProcessor()),
293
            'hostname'      => $this->monolog->pushProcessor(new HostnameProcessor()),
294
            'process_id'    => $this->monolog->pushProcessor(new ProcessIdProcessor()),
295
            'uid'           => $this->monolog->pushProcessor(new UidProcessor()),
296
            'memory_usage'  => $this->monolog->pushProcessor(new MemoryUsageProcessor()),
297
            'psr'           => $this->monolog->pushProcessor(new PsrLogMessageProcessor()),
298
            default         => throw new InvalidArgumentException('Invalid formatter for log processor. Accepts values: web/introspection/hostname/process_id/uid/memory_usage/psr'),
299
        };
300
    }
301
302
    /**
303
     * Definit le formateur des gestionnaire
304
     *
305
     * @param array $allowed Formats autorisés
306
     */
307
    private function setFormatter(object $handler, array $allowed, ?string $format = 'json'): object
308
    {
309
        if (! method_exists($handler, 'setFormatter')) {
310
            return $handler;
311
        }
312
313
        if ($format === null || $format === '') {
314
            $format = 'json';
315
        }
316
        if ($allowed !== [] && ! in_array($format, $allowed, true)) {
317
            throw new InvalidArgumentException('Invalid formatter for log file handler. Accepts values: ' . implode('/', $allowed));
318
        }
319
320
        switch ($format) {
321
            case 'json':
322
                $handler->setFormatter(new JsonFormatter());
323
                break;
324
325
            case 'line':
326
                $handler->setFormatter(new LineFormatter(null, $this->config->date_format ?? 'Y-m-d H:i:s'));
327
                break;
328
329
            case 'normalizer':
330
                $handler->setFormatter(new NormalizerFormatter($this->config->date_format ?? 'Y-m-d H:i:s'));
331
                break;
332
333
            case 'scalar':
334
                $handler->setFormatter(new ScalarFormatter($this->config->date_format ?? 'Y-m-d H:i:s'));
335
                break;
336
337
            case 'html':
338
                $handler->setFormatter(new HtmlFormatter($this->config->date_format ?? 'Y-m-d H:i:s'));
339
                break;
340
341
            default:
342
                break;
343
        }
344
345
        return $handler;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $handler returns the type object which is incompatible with the type-hinted return object.
Loading history...
346
    }
347
}
348