Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Log;
4
 
5
use Closure;
6
use Illuminate\Support\Str;
7
use InvalidArgumentException;
8
use Monolog\Formatter\LineFormatter;
9
use Monolog\Handler\ErrorLogHandler;
10
use Monolog\Handler\FingersCrossedHandler;
11
use Monolog\Handler\FormattableHandlerInterface;
12
use Monolog\Handler\HandlerInterface;
13
use Monolog\Handler\RotatingFileHandler;
14
use Monolog\Handler\SlackWebhookHandler;
15
use Monolog\Handler\StreamHandler;
16
use Monolog\Handler\SyslogHandler;
17
use Monolog\Handler\WhatFailureGroupHandler;
18
use Monolog\Logger as Monolog;
19
use Psr\Log\LoggerInterface;
20
use Throwable;
21
 
22
/**
23
 * @mixin \Illuminate\Log\Logger
24
 */
25
class LogManager implements LoggerInterface
26
{
27
    use ParsesLogConfiguration;
28
 
29
    /**
30
     * The application instance.
31
     *
32
     * @var \Illuminate\Contracts\Foundation\Application
33
     */
34
    protected $app;
35
 
36
    /**
37
     * The array of resolved channels.
38
     *
39
     * @var array
40
     */
41
    protected $channels = [];
42
 
43
    /**
44
     * The context shared across channels and stacks.
45
     *
46
     * @var array
47
     */
48
    protected $sharedContext = [];
49
 
50
    /**
51
     * The registered custom driver creators.
52
     *
53
     * @var array
54
     */
55
    protected $customCreators = [];
56
 
57
    /**
58
     * The standard date format to use when writing logs.
59
     *
60
     * @var string
61
     */
62
    protected $dateFormat = 'Y-m-d H:i:s';
63
 
64
    /**
65
     * Create a new Log manager instance.
66
     *
67
     * @param  \Illuminate\Contracts\Foundation\Application  $app
68
     * @return void
69
     */
70
    public function __construct($app)
71
    {
72
        $this->app = $app;
73
    }
74
 
75
    /**
76
     * Build an on-demand log channel.
77
     *
78
     * @param  array  $config
79
     * @return \Psr\Log\LoggerInterface
80
     */
81
    public function build(array $config)
82
    {
83
        unset($this->channels['ondemand']);
84
 
85
        return $this->get('ondemand', $config);
86
    }
87
 
88
    /**
89
     * Create a new, on-demand aggregate logger instance.
90
     *
91
     * @param  array  $channels
92
     * @param  string|null  $channel
93
     * @return \Psr\Log\LoggerInterface
94
     */
95
    public function stack(array $channels, $channel = null)
96
    {
97
        return (new Logger(
98
            $this->createStackDriver(compact('channels', 'channel')),
99
            $this->app['events']
100
        ))->withContext($this->sharedContext);
101
    }
102
 
103
    /**
104
     * Get a log channel instance.
105
     *
106
     * @param  string|null  $channel
107
     * @return \Psr\Log\LoggerInterface
108
     */
109
    public function channel($channel = null)
110
    {
111
        return $this->driver($channel);
112
    }
113
 
114
    /**
115
     * Get a log driver instance.
116
     *
117
     * @param  string|null  $driver
118
     * @return \Psr\Log\LoggerInterface
119
     */
120
    public function driver($driver = null)
121
    {
122
        return $this->get($this->parseDriver($driver));
123
    }
124
 
125
    /**
126
     * Attempt to get the log from the local cache.
127
     *
128
     * @param  string  $name
129
     * @param  array|null  $config
130
     * @return \Psr\Log\LoggerInterface
131
     */
132
    protected function get($name, ?array $config = null)
133
    {
134
        try {
135
            return $this->channels[$name] ?? with($this->resolve($name, $config), function ($logger) use ($name) {
136
                return $this->channels[$name] = $this->tap($name, new Logger($logger, $this->app['events']))->withContext($this->sharedContext);
137
            });
138
        } catch (Throwable $e) {
139
            return tap($this->createEmergencyLogger(), function ($logger) use ($e) {
140
                $logger->emergency('Unable to create configured logger. Using emergency logger.', [
141
                    'exception' => $e,
142
                ]);
143
            });
144
        }
145
    }
146
 
147
    /**
148
     * Apply the configured taps for the logger.
149
     *
150
     * @param  string  $name
151
     * @param  \Illuminate\Log\Logger  $logger
152
     * @return \Illuminate\Log\Logger
153
     */
154
    protected function tap($name, Logger $logger)
155
    {
156
        foreach ($this->configurationFor($name)['tap'] ?? [] as $tap) {
157
            [$class, $arguments] = $this->parseTap($tap);
158
 
159
            $this->app->make($class)->__invoke($logger, ...explode(',', $arguments));
160
        }
161
 
162
        return $logger;
163
    }
164
 
165
    /**
166
     * Parse the given tap class string into a class name and arguments string.
167
     *
168
     * @param  string  $tap
169
     * @return array
170
     */
171
    protected function parseTap($tap)
172
    {
173
        return str_contains($tap, ':') ? explode(':', $tap, 2) : [$tap, ''];
174
    }
175
 
176
    /**
177
     * Create an emergency log handler to avoid white screens of death.
178
     *
179
     * @return \Psr\Log\LoggerInterface
180
     */
181
    protected function createEmergencyLogger()
182
    {
183
        $config = $this->configurationFor('emergency');
184
 
185
        $handler = new StreamHandler(
186
            $config['path'] ?? $this->app->storagePath().'/logs/laravel.log',
187
            $this->level(['level' => 'debug'])
188
        );
189
 
190
        return new Logger(
191
            new Monolog('laravel', $this->prepareHandlers([$handler])),
192
            $this->app['events']
193
        );
194
    }
195
 
196
    /**
197
     * Resolve the given log instance by name.
198
     *
199
     * @param  string  $name
200
     * @param  array|null  $config
201
     * @return \Psr\Log\LoggerInterface
202
     *
203
     * @throws \InvalidArgumentException
204
     */
205
    protected function resolve($name, ?array $config = null)
206
    {
207
        $config ??= $this->configurationFor($name);
208
 
209
        if (is_null($config)) {
210
            throw new InvalidArgumentException("Log [{$name}] is not defined.");
211
        }
212
 
213
        if (isset($this->customCreators[$config['driver']])) {
214
            return $this->callCustomCreator($config);
215
        }
216
 
217
        $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
218
 
219
        if (method_exists($this, $driverMethod)) {
220
            return $this->{$driverMethod}($config);
221
        }
222
 
223
        throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
224
    }
225
 
226
    /**
227
     * Call a custom driver creator.
228
     *
229
     * @param  array  $config
230
     * @return mixed
231
     */
232
    protected function callCustomCreator(array $config)
233
    {
234
        return $this->customCreators[$config['driver']]($this->app, $config);
235
    }
236
 
237
    /**
238
     * Create a custom log driver instance.
239
     *
240
     * @param  array  $config
241
     * @return \Psr\Log\LoggerInterface
242
     */
243
    protected function createCustomDriver(array $config)
244
    {
245
        $factory = is_callable($via = $config['via']) ? $via : $this->app->make($via);
246
 
247
        return $factory($config);
248
    }
249
 
250
    /**
251
     * Create an aggregate log driver instance.
252
     *
253
     * @param  array  $config
254
     * @return \Psr\Log\LoggerInterface
255
     */
256
    protected function createStackDriver(array $config)
257
    {
258
        if (is_string($config['channels'])) {
259
            $config['channels'] = explode(',', $config['channels']);
260
        }
261
 
262
        $handlers = collect($config['channels'])->flatMap(function ($channel) {
263
            return $channel instanceof LoggerInterface
264
                ? $channel->getHandlers()
265
                : $this->channel($channel)->getHandlers();
266
        })->all();
267
 
268
        $processors = collect($config['channels'])->flatMap(function ($channel) {
269
            return $channel instanceof LoggerInterface
270
                ? $channel->getProcessors()
271
                : $this->channel($channel)->getProcessors();
272
        })->all();
273
 
274
        if ($config['ignore_exceptions'] ?? false) {
275
            $handlers = [new WhatFailureGroupHandler($handlers)];
276
        }
277
 
278
        return new Monolog($this->parseChannel($config), $handlers, $processors);
279
    }
280
 
281
    /**
282
     * Create an instance of the single file log driver.
283
     *
284
     * @param  array  $config
285
     * @return \Psr\Log\LoggerInterface
286
     */
287
    protected function createSingleDriver(array $config)
288
    {
289
        return new Monolog($this->parseChannel($config), [
290
            $this->prepareHandler(
291
                new StreamHandler(
292
                    $config['path'], $this->level($config),
293
                    $config['bubble'] ?? true, $config['permission'] ?? null, $config['locking'] ?? false
294
                ), $config
295
            ),
296
        ]);
297
    }
298
 
299
    /**
300
     * Create an instance of the daily file log driver.
301
     *
302
     * @param  array  $config
303
     * @return \Psr\Log\LoggerInterface
304
     */
305
    protected function createDailyDriver(array $config)
306
    {
307
        return new Monolog($this->parseChannel($config), [
308
            $this->prepareHandler(new RotatingFileHandler(
309
                $config['path'], $config['days'] ?? 7, $this->level($config),
310
                $config['bubble'] ?? true, $config['permission'] ?? null, $config['locking'] ?? false
311
            ), $config),
312
        ]);
313
    }
314
 
315
    /**
316
     * Create an instance of the Slack log driver.
317
     *
318
     * @param  array  $config
319
     * @return \Psr\Log\LoggerInterface
320
     */
321
    protected function createSlackDriver(array $config)
322
    {
323
        return new Monolog($this->parseChannel($config), [
324
            $this->prepareHandler(new SlackWebhookHandler(
325
                $config['url'],
326
                $config['channel'] ?? null,
327
                $config['username'] ?? 'Laravel',
328
                $config['attachment'] ?? true,
329
                $config['emoji'] ?? ':boom:',
330
                $config['short'] ?? false,
331
                $config['context'] ?? true,
332
                $this->level($config),
333
                $config['bubble'] ?? true,
334
                $config['exclude_fields'] ?? []
335
            ), $config),
336
        ]);
337
    }
338
 
339
    /**
340
     * Create an instance of the syslog log driver.
341
     *
342
     * @param  array  $config
343
     * @return \Psr\Log\LoggerInterface
344
     */
345
    protected function createSyslogDriver(array $config)
346
    {
347
        return new Monolog($this->parseChannel($config), [
348
            $this->prepareHandler(new SyslogHandler(
349
                Str::snake($this->app['config']['app.name'], '-'),
350
                $config['facility'] ?? LOG_USER, $this->level($config)
351
            ), $config),
352
        ]);
353
    }
354
 
355
    /**
356
     * Create an instance of the "error log" log driver.
357
     *
358
     * @param  array  $config
359
     * @return \Psr\Log\LoggerInterface
360
     */
361
    protected function createErrorlogDriver(array $config)
362
    {
363
        return new Monolog($this->parseChannel($config), [
364
            $this->prepareHandler(new ErrorLogHandler(
365
                $config['type'] ?? ErrorLogHandler::OPERATING_SYSTEM, $this->level($config)
366
            )),
367
        ]);
368
    }
369
 
370
    /**
371
     * Create an instance of any handler available in Monolog.
372
     *
373
     * @param  array  $config
374
     * @return \Psr\Log\LoggerInterface
375
     *
376
     * @throws \InvalidArgumentException
377
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
378
     */
379
    protected function createMonologDriver(array $config)
380
    {
381
        if (! is_a($config['handler'], HandlerInterface::class, true)) {
382
            throw new InvalidArgumentException(
383
                $config['handler'].' must be an instance of '.HandlerInterface::class
384
            );
385
        }
386
 
387
        $with = array_merge(
388
            ['level' => $this->level($config)],
389
            $config['with'] ?? [],
390
            $config['handler_with'] ?? []
391
        );
392
 
393
        return new Monolog($this->parseChannel($config), [$this->prepareHandler(
394
            $this->app->make($config['handler'], $with), $config
395
        )]);
396
    }
397
 
398
    /**
399
     * Prepare the handlers for usage by Monolog.
400
     *
401
     * @param  array  $handlers
402
     * @return array
403
     */
404
    protected function prepareHandlers(array $handlers)
405
    {
406
        foreach ($handlers as $key => $handler) {
407
            $handlers[$key] = $this->prepareHandler($handler);
408
        }
409
 
410
        return $handlers;
411
    }
412
 
413
    /**
414
     * Prepare the handler for usage by Monolog.
415
     *
416
     * @param  \Monolog\Handler\HandlerInterface  $handler
417
     * @param  array  $config
418
     * @return \Monolog\Handler\HandlerInterface
419
     */
420
    protected function prepareHandler(HandlerInterface $handler, array $config = [])
421
    {
422
        if (isset($config['action_level'])) {
423
            $handler = new FingersCrossedHandler(
424
                $handler,
425
                $this->actionLevel($config),
426
                0,
427
                true,
428
                $config['stop_buffering'] ?? true
429
            );
430
        }
431
 
432
        if (! $handler instanceof FormattableHandlerInterface) {
433
            return $handler;
434
        }
435
 
436
        if (! isset($config['formatter'])) {
437
            $handler->setFormatter($this->formatter());
438
        } elseif ($config['formatter'] !== 'default') {
439
            $handler->setFormatter($this->app->make($config['formatter'], $config['formatter_with'] ?? []));
440
        }
441
 
442
        return $handler;
443
    }
444
 
445
    /**
446
     * Get a Monolog formatter instance.
447
     *
448
     * @return \Monolog\Formatter\FormatterInterface
449
     */
450
    protected function formatter()
451
    {
452
        return tap(new LineFormatter(null, $this->dateFormat, true, true), function ($formatter) {
453
            $formatter->includeStacktraces();
454
        });
455
    }
456
 
457
    /**
458
     * Share context across channels and stacks.
459
     *
460
     * @param  array  $context
461
     * @return $this
462
     */
463
    public function shareContext(array $context)
464
    {
465
        foreach ($this->channels as $channel) {
466
            $channel->withContext($context);
467
        }
468
 
469
        $this->sharedContext = array_merge($this->sharedContext, $context);
470
 
471
        return $this;
472
    }
473
 
474
    /**
475
     * The context shared across channels and stacks.
476
     *
477
     * @return array
478
     */
479
    public function sharedContext()
480
    {
481
        return $this->sharedContext;
482
    }
483
 
484
    /**
485
     * Flush the shared context.
486
     *
487
     * @return $this
488
     */
489
    public function flushSharedContext()
490
    {
491
        $this->sharedContext = [];
492
 
493
        return $this;
494
    }
495
 
496
    /**
497
     * Get fallback log channel name.
498
     *
499
     * @return string
500
     */
501
    protected function getFallbackChannelName()
502
    {
503
        return $this->app->bound('env') ? $this->app->environment() : 'production';
504
    }
505
 
506
    /**
507
     * Get the log connection configuration.
508
     *
509
     * @param  string  $name
510
     * @return array
511
     */
512
    protected function configurationFor($name)
513
    {
514
        return $this->app['config']["logging.channels.{$name}"];
515
    }
516
 
517
    /**
518
     * Get the default log driver name.
519
     *
520
     * @return string|null
521
     */
522
    public function getDefaultDriver()
523
    {
524
        return $this->app['config']['logging.default'];
525
    }
526
 
527
    /**
528
     * Set the default log driver name.
529
     *
530
     * @param  string  $name
531
     * @return void
532
     */
533
    public function setDefaultDriver($name)
534
    {
535
        $this->app['config']['logging.default'] = $name;
536
    }
537
 
538
    /**
539
     * Register a custom driver creator Closure.
540
     *
541
     * @param  string  $driver
542
     * @param  \Closure  $callback
543
     * @return $this
544
     */
545
    public function extend($driver, Closure $callback)
546
    {
547
        $this->customCreators[$driver] = $callback->bindTo($this, $this);
548
 
549
        return $this;
550
    }
551
 
552
    /**
553
     * Unset the given channel instance.
554
     *
555
     * @param  string|null  $driver
399 lars 556
     * @return void
148 lars 557
     */
558
    public function forgetChannel($driver = null)
559
    {
560
        $driver = $this->parseDriver($driver);
561
 
562
        if (isset($this->channels[$driver])) {
563
            unset($this->channels[$driver]);
564
        }
565
    }
566
 
567
    /**
568
     * Parse the driver name.
569
     *
570
     * @param  string|null  $driver
571
     * @return string|null
572
     */
573
    protected function parseDriver($driver)
574
    {
575
        $driver ??= $this->getDefaultDriver();
576
 
577
        if ($this->app->runningUnitTests()) {
578
            $driver ??= 'null';
579
        }
580
 
581
        return $driver;
582
    }
583
 
584
    /**
585
     * Get all of the resolved log channels.
586
     *
587
     * @return array
588
     */
589
    public function getChannels()
590
    {
591
        return $this->channels;
592
    }
593
 
594
    /**
595
     * System is unusable.
596
     *
597
     * @param  string  $message
598
     * @param  array  $context
599
     * @return void
600
     */
601
    public function emergency($message, array $context = []): void
602
    {
603
        $this->driver()->emergency($message, $context);
604
    }
605
 
606
    /**
607
     * Action must be taken immediately.
608
     *
609
     * Example: Entire website down, database unavailable, etc. This should
610
     * trigger the SMS alerts and wake you up.
611
     *
612
     * @param  string  $message
613
     * @param  array  $context
614
     * @return void
615
     */
616
    public function alert($message, array $context = []): void
617
    {
618
        $this->driver()->alert($message, $context);
619
    }
620
 
621
    /**
622
     * Critical conditions.
623
     *
624
     * Example: Application component unavailable, unexpected exception.
625
     *
626
     * @param  string  $message
627
     * @param  array  $context
628
     * @return void
629
     */
630
    public function critical($message, array $context = []): void
631
    {
632
        $this->driver()->critical($message, $context);
633
    }
634
 
635
    /**
636
     * Runtime errors that do not require immediate action but should typically
637
     * be logged and monitored.
638
     *
639
     * @param  string  $message
640
     * @param  array  $context
641
     * @return void
642
     */
643
    public function error($message, array $context = []): void
644
    {
645
        $this->driver()->error($message, $context);
646
    }
647
 
648
    /**
649
     * Exceptional occurrences that are not errors.
650
     *
651
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
652
     * that are not necessarily wrong.
653
     *
654
     * @param  string  $message
655
     * @param  array  $context
656
     * @return void
657
     */
658
    public function warning($message, array $context = []): void
659
    {
660
        $this->driver()->warning($message, $context);
661
    }
662
 
663
    /**
664
     * Normal but significant events.
665
     *
666
     * @param  string  $message
667
     * @param  array  $context
668
     * @return void
669
     */
670
    public function notice($message, array $context = []): void
671
    {
672
        $this->driver()->notice($message, $context);
673
    }
674
 
675
    /**
676
     * Interesting events.
677
     *
678
     * Example: User logs in, SQL logs.
679
     *
680
     * @param  string  $message
681
     * @param  array  $context
682
     * @return void
683
     */
684
    public function info($message, array $context = []): void
685
    {
686
        $this->driver()->info($message, $context);
687
    }
688
 
689
    /**
690
     * Detailed debug information.
691
     *
692
     * @param  string  $message
693
     * @param  array  $context
694
     * @return void
695
     */
696
    public function debug($message, array $context = []): void
697
    {
698
        $this->driver()->debug($message, $context);
699
    }
700
 
701
    /**
702
     * Logs with an arbitrary level.
703
     *
704
     * @param  mixed  $level
705
     * @param  string  $message
706
     * @param  array  $context
707
     * @return void
708
     */
709
    public function log($level, $message, array $context = []): void
710
    {
711
        $this->driver()->log($level, $message, $context);
712
    }
713
 
714
    /**
715
     * Dynamically call the default driver instance.
716
     *
717
     * @param  string  $method
718
     * @param  array  $parameters
719
     * @return mixed
720
     */
721
    public function __call($method, $parameters)
722
    {
723
        return $this->driver()->$method(...$parameters);
724
    }
725
}