Вход Регистрация
Файл: vendor/laravel/framework/src/Illuminate/Log/LogManager.php
Строк: 978
<?php

namespace IlluminateLog;

use 
Closure;
use 
IlluminateSupportStr;
use 
InvalidArgumentException;
use 
MonologFormatterLineFormatter;
use 
MonologHandlerErrorLogHandler;
use 
MonologHandlerFingersCrossedHandler;
use 
MonologHandlerFormattableHandlerInterface;
use 
MonologHandlerHandlerInterface;
use 
MonologHandlerRotatingFileHandler;
use 
MonologHandlerSlackWebhookHandler;
use 
MonologHandlerStreamHandler;
use 
MonologHandlerSyslogHandler;
use 
MonologHandlerWhatFailureGroupHandler;
use 
MonologLogger as Monolog;
use 
MonologProcessorProcessorInterface;
use 
MonologProcessorPsrLogMessageProcessor;
use 
PsrLogLoggerInterface;
use 
Throwable;

/**
 * @mixin IlluminateLogLogger
 */
class LogManager implements LoggerInterface
{
    use 
ParsesLogConfiguration;

    
/**
     * The application instance.
     *
     * @var IlluminateContractsFoundationApplication
     */
    
protected $app;

    
/**
     * The array of resolved channels.
     *
     * @var array
     */
    
protected $channels = [];

    
/**
     * The context shared across channels and stacks.
     *
     * @var array
     */
    
protected $sharedContext = [];

    
/**
     * The registered custom driver creators.
     *
     * @var array
     */
    
protected $customCreators = [];

    
/**
     * The standard date format to use when writing logs.
     *
     * @var string
     */
    
protected $dateFormat 'Y-m-d H:i:s';

    
/**
     * Create a new Log manager instance.
     *
     * @param  IlluminateContractsFoundationApplication  $app
     * @return void
     */
    
public function __construct($app)
    {
        
$this->app $app;
    }

    
/**
     * Build an on-demand log channel.
     *
     * @param  array  $config
     * @return PsrLogLoggerInterface
     */
    
public function build(array $config)
    {
        unset(
$this->channels['ondemand']);

        return 
$this->get('ondemand'$config);
    }

    
/**
     * Create a new, on-demand aggregate logger instance.
     *
     * @param  array  $channels
     * @param  string|null  $channel
     * @return PsrLogLoggerInterface
     */
    
public function stack(array $channels$channel null)
    {
        return (new 
Logger(
            
$this->createStackDriver(compact('channels''channel')),
            
$this->app['events']
        ))->
withContext($this->sharedContext);
    }

    
/**
     * Get a log channel instance.
     *
     * @param  string|null  $channel
     * @return PsrLogLoggerInterface
     */
    
public function channel($channel null)
    {
        return 
$this->driver($channel);
    }

    
/**
     * Get a log driver instance.
     *
     * @param  string|null  $driver
     * @return PsrLogLoggerInterface
     */
    
public function driver($driver null)
    {
        return 
$this->get($this->parseDriver($driver));
    }

    
/**
     * Attempt to get the log from the local cache.
     *
     * @param  string  $name
     * @param  array|null  $config
     * @return PsrLogLoggerInterface
     */
    
protected function get($name, ?array $config null)
    {
        try {
            return 
$this->channels[$name] ?? with($this->resolve($name$config), function ($logger) use ($name) {
                return 
$this->channels[$name] = $this->tap($name, new Logger($logger$this->app['events']))->withContext($this->sharedContext);
            });
        } catch (
Throwable $e) {
            return 
tap($this->createEmergencyLogger(), function ($logger) use ($e) {
                
$logger->emergency('Unable to create configured logger. Using emergency logger.', [
                    
'exception' => $e,
                ]);
            });
        }
    }

    
/**
     * Apply the configured taps for the logger.
     *
     * @param  string  $name
     * @param  IlluminateLogLogger  $logger
     * @return IlluminateLogLogger
     */
    
protected function tap($nameLogger $logger)
    {
        foreach (
$this->configurationFor($name)['tap'] ?? [] as $tap) {
            [
$class$arguments] = $this->parseTap($tap);

            
$this->app->make($class)->__invoke($logger, ...explode(','$arguments));
        }

        return 
$logger;
    }

    
/**
     * Parse the given tap class string into a class name and arguments string.
     *
     * @param  string  $tap
     * @return array
     */
    
protected function parseTap($tap)
    {
        return 
str_contains($tap':') ? explode(':'$tap2) : [$tap''];
    }

    
/**
     * Create an emergency log handler to avoid white screens of death.
     *
     * @return PsrLogLoggerInterface
     */
    
protected function createEmergencyLogger()
    {
        
$config $this->configurationFor('emergency');

        
$handler = new StreamHandler(
            
$config['path'] ?? $this->app->storagePath().'/logs/laravel.log',
            
$this->level(['level' => 'debug'])
        );

        return new 
Logger(
            new 
Monolog('laravel'$this->prepareHandlers([$handler])),
            
$this->app['events']
        );
    }

    
/**
     * Resolve the given log instance by name.
     *
     * @param  string  $name
     * @param  array|null  $config
     * @return PsrLogLoggerInterface
     *
     * @throws InvalidArgumentException
     */
    
protected function resolve($name, ?array $config null)
    {
        
$config ??= $this->configurationFor($name);

        if (
is_null($config)) {
            throw new 
InvalidArgumentException("Log [{$name}] is not defined.");
        }

        if (isset(
$this->customCreators[$config['driver']])) {
            return 
$this->callCustomCreator($config);
        }

        
$driverMethod 'create'.ucfirst($config['driver']).'Driver';

        if (
method_exists($this$driverMethod)) {
            return 
$this->{$driverMethod}($config);
        }

        throw new 
InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
    }

    
/**
     * Call a custom driver creator.
     *
     * @param  array  $config
     * @return mixed
     */
    
protected function callCustomCreator(array $config)
    {
        return 
$this->customCreators[$config['driver']]($this->app$config);
    }

    
/**
     * Create a custom log driver instance.
     *
     * @param  array  $config
     * @return PsrLogLoggerInterface
     */
    
protected function createCustomDriver(array $config)
    {
        
$factory is_callable($via $config['via']) ? $via $this->app->make($via);

        return 
$factory($config);
    }

    
/**
     * Create an aggregate log driver instance.
     *
     * @param  array  $config
     * @return PsrLogLoggerInterface
     */
    
protected function createStackDriver(array $config)
    {
        if (
is_string($config['channels'])) {
            
$config['channels'] = explode(','$config['channels']);
        }

        
$handlers collect($config['channels'])->flatMap(function ($channel) {
            return 
$channel instanceof LoggerInterface
                
$channel->getHandlers()
                : 
$this->channel($channel)->getHandlers();
        })->
all();

        
$processors collect($config['channels'])->flatMap(function ($channel) {
            return 
$channel instanceof LoggerInterface
                
$channel->getProcessors()
                : 
$this->channel($channel)->getProcessors();
        })->
all();

        if (
$config['ignore_exceptions'] ?? false) {
            
$handlers = [new WhatFailureGroupHandler($handlers)];
        }

        return new 
Monolog($this->parseChannel($config), $handlers$processors);
    }

    
/**
     * Create an instance of the single file log driver.
     *
     * @param  array  $config
     * @return PsrLogLoggerInterface
     */
    
protected function createSingleDriver(array $config)
    {
        return new 
Monolog($this->parseChannel($config), [
            
$this->prepareHandler(
                new 
StreamHandler(
                    
$config['path'], $this->level($config),
                    
$config['bubble'] ?? true$config['permission'] ?? null$config['locking'] ?? false
                
), $config
            
),
        ], 
$config['replace_placeholders'] ?? false ? [new PsrLogMessageProcessor()] : []);
    }

    
/**
     * Create an instance of the daily file log driver.
     *
     * @param  array  $config
     * @return PsrLogLoggerInterface
     */
    
protected function createDailyDriver(array $config)
    {
        return new 
Monolog($this->parseChannel($config), [
            
$this->prepareHandler(new RotatingFileHandler(
                
$config['path'], $config['days'] ?? 7$this->level($config),
                
$config['bubble'] ?? true$config['permission'] ?? null$config['locking'] ?? false
            
), $config),
        ], 
$config['replace_placeholders'] ?? false ? [new PsrLogMessageProcessor()] : []);
    }

    
/**
     * Create an instance of the Slack log driver.
     *
     * @param  array  $config
     * @return PsrLogLoggerInterface
     */
    
protected function createSlackDriver(array $config)
    {
        return new 
Monolog($this->parseChannel($config), [
            
$this->prepareHandler(new SlackWebhookHandler(
                
$config['url'],
                
$config['channel'] ?? null,
                
$config['username'] ?? 'Laravel',
                
$config['attachment'] ?? true,
                
$config['emoji'] ?? ':boom:',
                
$config['short'] ?? false,
                
$config['context'] ?? true,
                
$this->level($config),
                
$config['bubble'] ?? true,
                
$config['exclude_fields'] ?? []
            ), 
$config),
        ], 
$config['replace_placeholders'] ?? false ? [new PsrLogMessageProcessor()] : []);
    }

    
/**
     * Create an instance of the syslog log driver.
     *
     * @param  array  $config
     * @return PsrLogLoggerInterface
     */
    
protected function createSyslogDriver(array $config)
    {
        return new 
Monolog($this->parseChannel($config), [
            
$this->prepareHandler(new SyslogHandler(
                
Str::snake($this->app['config']['app.name'], '-'),
                
$config['facility'] ?? LOG_USER$this->level($config)
            ), 
$config),
        ], 
$config['replace_placeholders'] ?? false ? [new PsrLogMessageProcessor()] : []);
    }

    
/**
     * Create an instance of the "error log" log driver.
     *
     * @param  array  $config
     * @return PsrLogLoggerInterface
     */
    
protected function createErrorlogDriver(array $config)
    {
        return new 
Monolog($this->parseChannel($config), [
            
$this->prepareHandler(new ErrorLogHandler(
                
$config['type'] ?? ErrorLogHandler::OPERATING_SYSTEM$this->level($config)
            )),
        ], 
$config['replace_placeholders'] ?? false ? [new PsrLogMessageProcessor()] : []);
    }

    
/**
     * Create an instance of any handler available in Monolog.
     *
     * @param  array  $config
     * @return PsrLogLoggerInterface
     *
     * @throws InvalidArgumentException
     * @throws IlluminateContractsContainerBindingResolutionException
     */
    
protected function createMonologDriver(array $config)
    {
        if (! 
is_a($config['handler'], HandlerInterface::class, true)) {
            throw new 
InvalidArgumentException(
                
$config['handler'].' must be an instance of '.HandlerInterface::class
            );
        }

        
collect($config['processors'] ?? [])->each(function ($processor) {
            
$processor $processor['processor'] ?? $processor;

            if (! 
is_a($processorProcessorInterface::class, true)) {
                throw new 
InvalidArgumentException(
                    
$processor.' must be an instance of '.ProcessorInterface::class
                );
            }
        });

        
$with array_merge(
            [
'level' => $this->level($config)],
            
$config['with'] ?? [],
            
$config['handler_with'] ?? []
        );

        
$handler $this->prepareHandler(
            
$this->app->make($config['handler'], $with), $config
        
);

        
$processors collect($config['processors'] ?? [])
            ->
map(fn ($processor) => $this->app->make($processor['processor'] ?? $processor$processor['with'] ?? []))
            ->
toArray();

        return new 
Monolog(
            
$this->parseChannel($config),
            [
$handler],
            
$processors,
        );
    }

    
/**
     * Prepare the handlers for usage by Monolog.
     *
     * @param  array  $handlers
     * @return array
     */
    
protected function prepareHandlers(array $handlers)
    {
        foreach (
$handlers as $key => $handler) {
            
$handlers[$key] = $this->prepareHandler($handler);
        }

        return 
$handlers;
    }

    
/**
     * Prepare the handler for usage by Monolog.
     *
     * @param  MonologHandlerHandlerInterface  $handler
     * @param  array  $config
     * @return MonologHandlerHandlerInterface
     */
    
protected function prepareHandler(HandlerInterface $handler, array $config = [])
    {
        if (isset(
$config['action_level'])) {
            
$handler = new FingersCrossedHandler(
                
$handler,
                
$this->actionLevel($config),
                
0,
                
true,
                
$config['stop_buffering'] ?? true
            
);
        }

        if (! 
$handler instanceof FormattableHandlerInterface) {
            return 
$handler;
        }

        if (! isset(
$config['formatter'])) {
            
$handler->setFormatter($this->formatter());
        } elseif (
$config['formatter'] !== 'default') {
            
$handler->setFormatter($this->app->make($config['formatter'], $config['formatter_with'] ?? []));
        }

        return 
$handler;
    }

    
/**
     * Get a Monolog formatter instance.
     *
     * @return MonologFormatterFormatterInterface
     */
    
protected function formatter()
    {
        return new 
LineFormatter(null$this->dateFormattruetruetrue);
    }

    
/**
     * Share context across channels and stacks.
     *
     * @param  array  $context
     * @return $this
     */
    
public function shareContext(array $context)
    {
        foreach (
$this->channels as $channel) {
            
$channel->withContext($context);
        }

        
$this->sharedContext array_merge($this->sharedContext$context);

        return 
$this;
    }

    
/**
     * The context shared across channels and stacks.
     *
     * @return array
     */
    
public function sharedContext()
    {
        return 
$this->sharedContext;
    }

    
/**
     * Flush the log context on all currently resolved channels.
     *
     * @return $this
     */
    
public function withoutContext()
    {
        foreach (
$this->channels as $channel) {
            if (
method_exists($channel'withoutContext')) {
                
$channel->withoutContext();
            }
        }

        return 
$this;
    }

    
/**
     * Flush the shared context.
     *
     * @return $this
     */
    
public function flushSharedContext()
    {
        
$this->sharedContext = [];

        return 
$this;
    }

    
/**
     * Get fallback log channel name.
     *
     * @return string
     */
    
protected function getFallbackChannelName()
    {
        return 
$this->app->bound('env') ? $this->app->environment() : 'production';
    }

    
/**
     * Get the log connection configuration.
     *
     * @param  string  $name
     * @return array
     */
    
protected function configurationFor($name)
    {
        return 
$this->app['config']["logging.channels.{$name}"];
    }

    
/**
     * Get the default log driver name.
     *
     * @return string|null
     */
    
public function getDefaultDriver()
    {
        return 
$this->app['config']['logging.default'];
    }

    
/**
     * Set the default log driver name.
     *
     * @param  string  $name
     * @return void
     */
    
public function setDefaultDriver($name)
    {
        
$this->app['config']['logging.default'] = $name;
    }

    
/**
     * Register a custom driver creator Closure.
     *
     * @param  string  $driver
     * @param  Closure  $callback
     * @return $this
     */
    
public function extend($driverClosure $callback)
    {
        
$this->customCreators[$driver] = $callback->bindTo($this$this);

        return 
$this;
    }

    
/**
     * Unset the given channel instance.
     *
     * @param  string|null  $driver
     * @return void
     */
    
public function forgetChannel($driver null)
    {
        
$driver $this->parseDriver($driver);

        if (isset(
$this->channels[$driver])) {
            unset(
$this->channels[$driver]);
        }
    }

    
/**
     * Parse the driver name.
     *
     * @param  string|null  $driver
     * @return string|null
     */
    
protected function parseDriver($driver)
    {
        
$driver ??= $this->getDefaultDriver();

        if (
$this->app->runningUnitTests()) {
            
$driver ??= 'null';
        }

        return 
$driver;
    }

    
/**
     * Get all of the resolved log channels.
     *
     * @return array
     */
    
public function getChannels()
    {
        return 
$this->channels;
    }

    
/**
     * System is unusable.
     *
     * @param  string|Stringable  $message
     * @param  array  $context
     * @return void
     */
    
public function emergency($message, array $context = []): void
    
{
        
$this->driver()->emergency($message$context);
    }

    
/**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param  string|Stringable  $message
     * @param  array  $context
     * @return void
     */
    
public function alert($message, array $context = []): void
    
{
        
$this->driver()->alert($message$context);
    }

    
/**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param  string|Stringable  $message
     * @param  array  $context
     * @return void
     */
    
public function critical($message, array $context = []): void
    
{
        
$this->driver()->critical($message$context);
    }

    
/**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param  string|Stringable  $message
     * @param  array  $context
     * @return void
     */
    
public function error($message, array $context = []): void
    
{
        
$this->driver()->error($message$context);
    }

    
/**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param  string|Stringable  $message
     * @param  array  $context
     * @return void
     */
    
public function warning($message, array $context = []): void
    
{
        
$this->driver()->warning($message$context);
    }

    
/**
     * Normal but significant events.
     *
     * @param  string|Stringable  $message
     * @param  array  $context
     * @return void
     */
    
public function notice($message, array $context = []): void
    
{
        
$this->driver()->notice($message$context);
    }

    
/**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param  string|Stringable  $message
     * @param  array  $context
     * @return void
     */
    
public function info($message, array $context = []): void
    
{
        
$this->driver()->info($message$context);
    }

    
/**
     * Detailed debug information.
     *
     * @param  string|Stringable  $message
     * @param  array  $context
     * @return void
     */
    
public function debug($message, array $context = []): void
    
{
        
$this->driver()->debug($message$context);
    }

    
/**
     * Logs with an arbitrary level.
     *
     * @param  mixed  $level
     * @param  string|Stringable  $message
     * @param  array  $context
     * @return void
     */
    
public function log($level$message, array $context = []): void
    
{
        
$this->driver()->log($level$message$context);
    }

    
/**
     * Dynamically call the default driver instance.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    
public function __call($method$parameters)
    {
        return 
$this->driver()->$method(...$parameters);
    }
}
Онлайн: 0
Реклама