Monolog::DEBUG, 'info' => Monolog::INFO, 'notice' => Monolog::NOTICE, 'warning' => Monolog::WARNING, 'error' => Monolog::ERROR, 'critical' => Monolog::CRITICAL, 'alert' => Monolog::ALERT, 'emergency' => Monolog::EMERGENCY, ]; /** * Create a new Log manager instance. * * @param \Illuminate\Foundation\Application $app * @return void */ public function __construct($app) { $this->app = $app; } /** * Create a new, on-demand aggregate logger instance. * * @param array $channels * @param string|null $channel * @return \Psr\Log\LoggerInterface */ public function stack(array $channels, $channel = null) { return new Logger( $this->createStackDriver(compact('channels', 'channel')), $this->app['events'] ); } /** * Get a log channel instance. * * @param string|null $channel * @return mixed */ public function channel($channel = null) { return $this->driver($channel); } /** * Get a log driver instance. * * @param string|null $driver * @return mixed */ public function driver($driver = null) { return $this->get($driver ?? $this->getDefaultDriver()); } /** * Attempt to get the log from the local cache. * * @param string $name * @return \Psr\Log\LoggerInterface */ protected function get($name) { try { return $this->channels[$name] ?? with($this->resolve($name), function ($logger) use ($name) { return $this->channels[$name] = $this->tap($name, new Logger($logger, $this->app['events'])); }); } 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 \Illuminate\Log\Logger $logger * @return \Illuminate\Log\Logger */ protected function tap($name, Logger $logger) { foreach ($this->configurationFor($name)['tap'] ?? [] as $tap) { list($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(':', $tap, 2) : [$tap, '']; } /** * Create an emergency log handler to avoid white screens of death. * * @return \Psr\Log\LoggerInterface */ protected function createEmergencyLogger() { return new Logger(new Monolog('laravel', $this->prepareHandlers([new StreamHandler( $this->app->storagePath().'/logs/laravel.log', $this->level(['level' => 'debug']) )])), $this->app['events']); } /** * Resolve the given log instance by name. * * @param string $name * @return \Psr\Log\LoggerInterface * * @throws \InvalidArgumentException */ protected function resolve($name) { $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 \Psr\Log\LoggerInterface */ 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 \Psr\Log\LoggerInterface */ protected function createStackDriver(array $config) { $handlers = collect($config['channels'])->flatMap(function ($channel) { return $this->channel($channel)->getHandlers(); })->all(); return new Monolog($this->parseChannel($config), $handlers); } /** * Create an instance of the single file log driver. * * @param array $config * @return \Psr\Log\LoggerInterface */ 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 ) ), ]); } /** * Create an instance of the daily file log driver. * * @param array $config * @return \Psr\Log\LoggerInterface */ 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 )), ]); } /** * Create an instance of the Slack log driver. * * @param array $config * @return \Psr\Log\LoggerInterface */ 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) )), ]); } /** * Create an instance of the syslog log driver. * * @param array $config * @return \Psr\Log\LoggerInterface */ protected function createSyslogDriver(array $config) { return new Monolog($this->parseChannel($config), [ $this->prepareHandler(new SyslogHandler( $this->app['config']['app.name'], $config['facility'] ?? LOG_USER, $this->level($config)) ), ]); } /** * Create an instance of the "error log" log driver. * * @param array $config * @return \Psr\Log\LoggerInterface */ protected function createErrorlogDriver(array $config) { return new Monolog($this->parseChannel($config), [ $this->prepareHandler(new ErrorLogHandler( $config['type'] ?? ErrorLogHandler::OPERATING_SYSTEM, $this->level($config)) ), ]); } /** * Create an instance of any handler available in Monolog. * * @param array $config * @return \Psr\Log\LoggerInterface * * @throws \InvalidArgumentException * @throws \Illuminate\Contracts\Container\BindingResolutionException */ 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 ); } $with = array_merge($config['with'] ?? [], $config['handler_with'] ?? []); return new Monolog($this->parseChannel($config), [$this->prepareHandler( $this->app->make($config['handler'], $with), $config )]); } /** * 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 \Monolog\Handler\HandlerInterface $handler * @param array $config * @return \Monolog\Handler\HandlerInterface */ protected function prepareHandler(HandlerInterface $handler, array $config = []) { 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 \Monolog\Formatter\FormatterInterface */ protected function formatter() { return tap(new LineFormatter(null, null, true, true), function ($formatter) { $formatter->includeStacktraces(); }); } /** * Extract the log channel from the given configuration. * * @param array $config * @return string */ protected function parseChannel(array $config) { if (! isset($config['name'])) { return $this->app->bound('env') ? $this->app->environment() : 'production'; } return $config['name']; } /** * Parse the string level into a Monolog constant. * * @param array $config * @return int * * @throws \InvalidArgumentException */ protected function level(array $config) { $level = $config['level'] ?? 'debug'; if (isset($this->levels[$level])) { return $this->levels[$level]; } throw new InvalidArgumentException('Invalid log level.'); } /** * 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 */ 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($driver, Closure $callback) { $this->customCreators[$driver] = $callback->bindTo($this, $this); return $this; } /** * System is unusable. * * @param string $message * @param array $context * * @return void */ public function emergency($message, array $context = []) { return $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 $message * @param array $context * * @return void */ public function alert($message, array $context = []) { return $this->driver()->alert($message, $context); } /** * Critical conditions. * * Example: Application component unavailable, unexpected exception. * * @param string $message * @param array $context * * @return void */ public function critical($message, array $context = []) { return $this->driver()->critical($message, $context); } /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message * @param array $context * * @return void */ public function error($message, array $context = []) { return $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 $message * @param array $context * * @return void */ public function warning($message, array $context = []) { return $this->driver()->warning($message, $context); } /** * Normal but significant events. * * @param string $message * @param array $context * * @return void */ public function notice($message, array $context = []) { return $this->driver()->notice($message, $context); } /** * Interesting events. * * Example: User logs in, SQL logs. * * @param string $message * @param array $context * * @return void */ public function info($message, array $context = []) { return $this->driver()->info($message, $context); } /** * Detailed debug information. * * @param string $message * @param array $context * * @return void */ public function debug($message, array $context = []) { return $this->driver()->debug($message, $context); } /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message * @param array $context * * @return void */ public function log($level, $message, array $context = []) { return $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); } }