vendor and env first commit

This commit is contained in:
2025-03-28 08:52:46 +01:00
parent f8388bc81b
commit 8f26283832
10976 changed files with 1349952 additions and 2 deletions
@@ -0,0 +1,42 @@
<?php
namespace Illuminate\Log\Context;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\Queue;
use Illuminate\Support\Facades\Context;
use Illuminate\Support\ServiceProvider;
class ContextServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->scoped(Repository::class);
}
/**
* Boot the application services.
*
* @return void
*/
public function boot()
{
Queue::createPayloadUsing(function ($connection, $queue, $payload) {
$context = Context::dehydrate();
return $context === null ? $payload : [
...$payload,
'illuminate:log:context' => $context,
];
});
$this->app['events']->listen(function (JobProcessing $event) {
Context::hydrate($event->job->payload()['illuminate:log:context'] ?? null);
});
}
}
@@ -0,0 +1,23 @@
<?php
namespace Illuminate\Log\Context\Events;
class ContextDehydrating
{
/**
* The context instance.
*
* @var \Illuminate\Log\Context\Repository
*/
public $context;
/**
* Create a new event instance.
*
* @param \Illuminate\Log\Context\Repository $context
*/
public function __construct($context)
{
$this->context = $context;
}
}
@@ -0,0 +1,23 @@
<?php
namespace Illuminate\Log\Context\Events;
class ContextHydrated
{
/**
* The context instance.
*
* @var \Illuminate\Log\Context\Repository
*/
public $context;
/**
* Create a new event instance.
*
* @param \Illuminate\Log\Context\Repository $context
*/
public function __construct($context)
{
$this->context = $context;
}
}
@@ -0,0 +1,470 @@
<?php
namespace Illuminate\Log\Context;
use __PHP_Incomplete_Class;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Log\Context\Events\ContextDehydrating as Dehydrating;
use Illuminate\Log\Context\Events\ContextHydrated as Hydrated;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\Macroable;
use RuntimeException;
use Throwable;
class Repository
{
use Conditionable, Macroable, SerializesModels;
/**
* The event dispatcher instance.
*
* @var \Illuminate\Events\Dispatcher
*/
protected $events;
/**
* The contextual data.
*
* @var array<string, mixed>
*/
protected $data = [];
/**
* The hidden contextual data.
*
* @var array<string, mixed>
*/
protected $hidden = [];
/**
* The callback that should handle unserialize exceptions.
*
* @var callable|null
*/
protected static $handleUnserializeExceptionsUsing;
/**
* Create a new Context instance.
*/
public function __construct(Dispatcher $events)
{
$this->events = $events;
}
/**
* Determine if the given key exists.
*
* @param string $key
* @return bool
*/
public function has($key)
{
return array_key_exists($key, $this->data);
}
/**
* Determine if the given key exists within the hidden context data.
*
* @param string $key
* @return bool
*/
public function hasHidden($key)
{
return array_key_exists($key, $this->hidden);
}
/**
* Retrieve all the context data.
*
* @return array<string, mixed>
*/
public function all()
{
return $this->data;
}
/**
* Retrieve all the hidden context data.
*
* @return array<string, mixed>
*/
public function allHidden()
{
return $this->hidden;
}
/**
* Retrieve the given key's value.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null)
{
return $this->data[$key] ?? value($default);
}
/**
* Retrieve the given key's hidden value.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function getHidden($key, $default = null)
{
return $this->hidden[$key] ?? value($default);
}
/**
* Retrieve the given key's value and then forget it.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function pull($key, $default = null)
{
return tap($this->get($key, $default), function () use ($key) {
$this->forget($key);
});
}
/**
* Retrieve the given key's hidden value and then forget it.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function pullHidden($key, $default = null)
{
return tap($this->getHidden($key, $default), function () use ($key) {
$this->forgetHidden($key);
});
}
/**
* Retrieve only the values of the given keys.
*
* @param array<int, string> $keys
* @return array<string, mixed>
*/
public function only($keys)
{
return array_intersect_key($this->data, array_flip($keys));
}
/**
* Retrieve only the hidden values of the given keys.
*
* @param array<int, string> $keys
* @return array<string, mixed>
*/
public function onlyHidden($keys)
{
return array_intersect_key($this->hidden, array_flip($keys));
}
/**
* Add a context value.
*
* @param string|array<string, mixed> $key
* @param mixed $value
* @return $this
*/
public function add($key, $value = null)
{
$this->data = array_merge(
$this->data,
is_array($key) ? $key : [$key => $value]
);
return $this;
}
/**
* Add a hidden context value.
*
* @param string|array<string, mixed> $key
* @param mixed $value
* @return $this
*/
public function addHidden($key, #[\SensitiveParameter] $value = null)
{
$this->hidden = array_merge(
$this->hidden,
is_array($key) ? $key : [$key => $value]
);
return $this;
}
/**
* Forget the given context key.
*
* @param string|array<int, string> $key
* @return $this
*/
public function forget($key)
{
foreach ((array) $key as $k) {
unset($this->data[$k]);
}
return $this;
}
/**
* Forget the given hidden context key.
*
* @param string|array<int, string> $key
* @return $this
*/
public function forgetHidden($key)
{
foreach ((array) $key as $k) {
unset($this->hidden[$k]);
}
return $this;
}
/**
* Add a context value if it does not exist yet.
*
* @param string $key
* @param mixed $value
* @return $this
*/
public function addIf($key, $value)
{
if (! $this->has($key)) {
$this->add($key, $value);
}
return $this;
}
/**
* Add a hidden context value if it does not exist yet.
*
* @param string $key
* @param mixed $value
* @return $this
*/
public function addHiddenIf($key, #[\SensitiveParameter] $value)
{
if (! $this->hasHidden($key)) {
$this->addHidden($key, $value);
}
return $this;
}
/**
* Push the given values onto the key's stack.
*
* @param string $key
* @param mixed ...$values
* @return $this
*
* @throws \RuntimeException
*/
public function push($key, ...$values)
{
if (! $this->isStackable($key)) {
throw new RuntimeException("Unable to push value onto context stack for key [{$key}].");
}
$this->data[$key] = [
...$this->data[$key] ?? [],
...$values,
];
return $this;
}
/**
* Push the given hidden values onto the key's stack.
*
* @param string $key
* @param mixed ...$values
* @return $this
*
* @throws \RuntimeException
*/
public function pushHidden($key, ...$values)
{
if (! $this->isHiddenStackable($key)) {
throw new RuntimeException("Unable to push value onto hidden context stack for key [{$key}].");
}
$this->hidden[$key] = [
...$this->hidden[$key] ?? [],
...$values,
];
return $this;
}
/**
* Determine if a given key can be used as a stack.
*
* @param string $key
* @return bool
*/
protected function isStackable($key)
{
return ! $this->has($key) ||
(is_array($this->data[$key]) && array_is_list($this->data[$key]));
}
/**
* Determine if a given key can be used as a hidden stack.
*
* @param string $key
* @return bool
*/
protected function isHiddenStackable($key)
{
return ! $this->hasHidden($key) ||
(is_array($this->hidden[$key]) && array_is_list($this->hidden[$key]));
}
/**
* Determine if the repository is empty.
*
* @return bool
*/
public function isEmpty()
{
return $this->all() === [] && $this->allHidden() === [];
}
/**
* Execute the given callback when context is about to be dehydrated.
*
* @param callable $callback
* @return $this
*/
public function dehydrating($callback)
{
$this->events->listen(fn (Dehydrating $event) => $callback($event->context));
return $this;
}
/**
* Execute the given callback when context has been hydrated.
*
* @param callable $callback
* @return $this
*/
public function hydrated($callback)
{
$this->events->listen(fn (Hydrated $event) => $callback($event->context));
return $this;
}
/**
* Handle unserialize exceptions using the given callback.
*
* @param callable|null $callback
* @return static
*/
public function handleUnserializeExceptionsUsing($callback)
{
static::$handleUnserializeExceptionsUsing = $callback;
return $this;
}
/**
* Flush all context data.
*
* @return $this
*/
public function flush()
{
$this->data = [];
$this->hidden = [];
return $this;
}
/**
* Dehydrate the context data.
*
* @internal
*
* @return ?array
*/
public function dehydrate()
{
$instance = (new static($this->events))
->add($this->all())
->addHidden($this->allHidden());
$instance->events->dispatch(new Dehydrating($instance));
$serialize = fn ($value) => serialize($instance->getSerializedPropertyValue($value, withRelations: false));
return $instance->isEmpty() ? null : [
'data' => array_map($serialize, $instance->all()),
'hidden' => array_map($serialize, $instance->allHidden()),
];
}
/**
* Hydrate the context instance.
*
* @internal
*
* @param ?array $context
* @return $this
*
* @throws \RuntimeException
*/
public function hydrate($context)
{
$unserialize = function ($value, $key, $hidden) {
try {
return tap($this->getRestoredPropertyValue(unserialize($value)), function ($value) {
if ($value instanceof __PHP_Incomplete_Class) {
throw new RuntimeException('Value is incomplete class: '.json_encode($value));
}
});
} catch (Throwable $e) {
if (static::$handleUnserializeExceptionsUsing !== null) {
return (static::$handleUnserializeExceptionsUsing)($e, $key, $value, $hidden);
}
if ($e instanceof ModelNotFoundException) {
if (function_exists('report')) {
report($e);
}
return null;
}
throw $e;
}
};
[$data, $hidden] = [
collect($context['data'] ?? [])->map(fn ($value, $key) => $unserialize($value, $key, false))->all(),
collect($context['hidden'] ?? [])->map(fn ($value, $key) => $unserialize($value, $key, true))->all(),
];
$this->events->dispatch(new Hydrated(
$this->flush()->add($data)->addHidden($hidden)
));
return $this;
}
}
@@ -0,0 +1,42 @@
<?php
namespace Illuminate\Log\Events;
class MessageLogged
{
/**
* The log "level".
*
* @var string
*/
public $level;
/**
* The log message.
*
* @var string
*/
public $message;
/**
* The log context.
*
* @var array
*/
public $context;
/**
* Create a new event instance.
*
* @param string $level
* @param string $message
* @param array $context
* @return void
*/
public function __construct($level, $message, array $context = [])
{
$this->level = $level;
$this->message = $message;
$this->context = $context;
}
}
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,793 @@
<?php
namespace Illuminate\Log;
use Closure;
use Illuminate\Log\Context\Repository as ContextRepository;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\ErrorLogHandler;
use Monolog\Handler\FingersCrossedHandler;
use Monolog\Handler\FormattableHandlerInterface;
use Monolog\Handler\HandlerInterface;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\SlackWebhookHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogHandler;
use Monolog\Handler\WhatFailureGroupHandler;
use Monolog\Logger as Monolog;
use Monolog\Processor\ProcessorInterface;
use Monolog\Processor\PsrLogMessageProcessor;
use Psr\Log\LoggerInterface;
use Throwable;
/**
* @mixin \Illuminate\Log\Logger
*/
class LogManager implements LoggerInterface
{
use ParsesLogConfiguration;
/**
* The application instance.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
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 \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
{
$this->app = $app;
}
/**
* Build an on-demand log channel.
*
* @param array $config
* @return \Psr\Log\LoggerInterface
*/
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 \Psr\Log\LoggerInterface
*/
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 \Psr\Log\LoggerInterface
*/
public function channel($channel = null)
{
return $this->driver($channel);
}
/**
* Get a log driver instance.
*
* @param string|null $driver
* @return \Psr\Log\LoggerInterface
*/
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 \Psr\Log\LoggerInterface
*/
protected function get($name, ?array $config = null)
{
try {
return $this->channels[$name] ?? with($this->resolve($name, $config), function ($logger) use ($name) {
$loggerWithContext = $this->tap(
$name,
new Logger($logger, $this->app['events'])
)->withContext($this->sharedContext);
if (method_exists($loggerWithContext->getLogger(), 'pushProcessor')) {
$loggerWithContext->pushProcessor(function ($record) {
if (! $this->app->bound(ContextRepository::class)) {
return $record;
}
return $record->with(extra: [
...$record->extra,
...$this->app[ContextRepository::class]->all(),
]);
});
}
return $this->channels[$name] = $loggerWithContext;
});
} 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) {
[$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()
{
$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 \Psr\Log\LoggerInterface
*
* @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 \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)
{
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 \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
), $config
),
], $config['replace_placeholders'] ?? false ? [new PsrLogMessageProcessor()] : []);
}
/**
* 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
), $config),
], $config['replace_placeholders'] ?? false ? [new PsrLogMessageProcessor()] : []);
}
/**
* 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),
$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 \Psr\Log\LoggerInterface
*/
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 \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)
)),
], $config['replace_placeholders'] ?? false ? [new PsrLogMessageProcessor()] : []);
}
/**
* 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
);
}
collect($config['processors'] ?? [])->each(function ($processor) {
$processor = $processor['processor'] ?? $processor;
if (! is_a($processor, ProcessorInterface::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 \Monolog\Handler\HandlerInterface $handler
* @param array $config
* @return \Monolog\Handler\HandlerInterface
*/
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 \Monolog\Formatter\FormatterInterface
*/
protected function formatter()
{
return new LineFormatter(null, $this->dateFormat, true, true, true);
}
/**
* 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($driver, Closure $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);
}
/**
* Set the application instance used by the manager.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return $this
*/
public function setApplication($app)
{
$this->app = $app;
return $this;
}
/**
* 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,0 +1,18 @@
<?php
namespace Illuminate\Log;
use Illuminate\Support\ServiceProvider;
class LogServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('log', fn ($app) => new LogManager($app));
}
}
+311
View File
@@ -0,0 +1,311 @@
<?php
namespace Illuminate\Log;
use Closure;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Log\Events\MessageLogged;
use Illuminate\Support\Traits\Conditionable;
use Psr\Log\LoggerInterface;
use RuntimeException;
class Logger implements LoggerInterface
{
use Conditionable;
/**
* The underlying logger implementation.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* The event dispatcher instance.
*
* @var \Illuminate\Contracts\Events\Dispatcher|null
*/
protected $dispatcher;
/**
* Any context to be added to logs.
*
* @var array
*/
protected $context = [];
/**
* Create a new log writer instance.
*
* @param \Psr\Log\LoggerInterface $logger
* @param \Illuminate\Contracts\Events\Dispatcher|null $dispatcher
* @return void
*/
public function __construct(LoggerInterface $logger, ?Dispatcher $dispatcher = null)
{
$this->logger = $logger;
$this->dispatcher = $dispatcher;
}
/**
* Log an emergency message to the logs.
*
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function emergency($message, array $context = []): void
{
$this->writeLog(__FUNCTION__, $message, $context);
}
/**
* Log an alert message to the logs.
*
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function alert($message, array $context = []): void
{
$this->writeLog(__FUNCTION__, $message, $context);
}
/**
* Log a critical message to the logs.
*
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function critical($message, array $context = []): void
{
$this->writeLog(__FUNCTION__, $message, $context);
}
/**
* Log an error message to the logs.
*
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function error($message, array $context = []): void
{
$this->writeLog(__FUNCTION__, $message, $context);
}
/**
* Log a warning message to the logs.
*
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function warning($message, array $context = []): void
{
$this->writeLog(__FUNCTION__, $message, $context);
}
/**
* Log a notice to the logs.
*
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function notice($message, array $context = []): void
{
$this->writeLog(__FUNCTION__, $message, $context);
}
/**
* Log an informational message to the logs.
*
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function info($message, array $context = []): void
{
$this->writeLog(__FUNCTION__, $message, $context);
}
/**
* Log a debug message to the logs.
*
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function debug($message, array $context = []): void
{
$this->writeLog(__FUNCTION__, $message, $context);
}
/**
* Log a message to the logs.
*
* @param string $level
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function log($level, $message, array $context = []): void
{
$this->writeLog($level, $message, $context);
}
/**
* Dynamically pass log calls into the writer.
*
* @param string $level
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
public function write($level, $message, array $context = []): void
{
$this->writeLog($level, $message, $context);
}
/**
* Write a message to the log.
*
* @param string $level
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @param array $context
* @return void
*/
protected function writeLog($level, $message, $context): void
{
$this->logger->{$level}(
$message = $this->formatMessage($message),
$context = array_merge($this->context, $context)
);
$this->fireLogEvent($level, $message, $context);
}
/**
* Add context to all future logs.
*
* @param array $context
* @return $this
*/
public function withContext(array $context = [])
{
$this->context = array_merge($this->context, $context);
return $this;
}
/**
* Flush the existing context array.
*
* @return $this
*/
public function withoutContext()
{
$this->context = [];
return $this;
}
/**
* Register a new callback handler for when a log event is triggered.
*
* @param \Closure $callback
* @return void
*
* @throws \RuntimeException
*/
public function listen(Closure $callback)
{
if (! isset($this->dispatcher)) {
throw new RuntimeException('Events dispatcher has not been set.');
}
$this->dispatcher->listen(MessageLogged::class, $callback);
}
/**
* Fires a log event.
*
* @param string $level
* @param string $message
* @param array $context
* @return void
*/
protected function fireLogEvent($level, $message, array $context = [])
{
// If the event dispatcher is set, we will pass along the parameters to the
// log listeners. These are useful for building profilers or other tools
// that aggregate all of the log messages for a given "request" cycle.
$this->dispatcher?->dispatch(new MessageLogged($level, $message, $context));
}
/**
* Format the parameters for the logger.
*
* @param \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message
* @return string
*/
protected function formatMessage($message)
{
if (is_array($message)) {
return var_export($message, true);
} elseif ($message instanceof Jsonable) {
return $message->toJson();
} elseif ($message instanceof Arrayable) {
return var_export($message->toArray(), true);
}
return (string) $message;
}
/**
* Get the underlying logger implementation.
*
* @return \Psr\Log\LoggerInterface
*/
public function getLogger()
{
return $this->logger;
}
/**
* Get the event dispatcher instance.
*
* @return \Illuminate\Contracts\Events\Dispatcher
*/
public function getEventDispatcher()
{
return $this->dispatcher;
}
/**
* Set the event dispatcher instance.
*
* @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
* @return void
*/
public function setEventDispatcher(Dispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* Dynamically proxy method calls to the underlying logger.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->logger->{$method}(...$parameters);
}
}
@@ -0,0 +1,81 @@
<?php
namespace Illuminate\Log;
use InvalidArgumentException;
use Monolog\Level;
trait ParsesLogConfiguration
{
/**
* The Log levels.
*
* @var array
*/
protected $levels = [
'debug' => Level::Debug,
'info' => Level::Info,
'notice' => Level::Notice,
'warning' => Level::Warning,
'error' => Level::Error,
'critical' => Level::Critical,
'alert' => Level::Alert,
'emergency' => Level::Emergency,
];
/**
* Get fallback log channel name.
*
* @return string
*/
abstract protected function getFallbackChannelName();
/**
* 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.');
}
/**
* Parse the action level from the given configuration.
*
* @param array $config
* @return int
*
* @throws \InvalidArgumentException
*/
protected function actionLevel(array $config)
{
$level = $config['action_level'] ?? 'debug';
if (isset($this->levels[$level])) {
return $this->levels[$level];
}
throw new InvalidArgumentException('Invalid log action level.');
}
/**
* Extract the log channel from the given configuration.
*
* @param array $config
* @return string
*/
protected function parseChannel(array $config)
{
return $config['name'] ?? $this->getFallbackChannelName();
}
}
@@ -0,0 +1,40 @@
{
"name": "illuminate/log",
"description": "The Illuminate Log package.",
"license": "MIT",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^8.2",
"illuminate/contracts": "^11.0",
"illuminate/support": "^11.0",
"monolog/monolog": "^3.0",
"psr/log": "^1.0|^2.0|^3.0"
},
"provide": {
"psr/log-implementation": "1.0|2.0|3.0"
},
"autoload": {
"psr-4": {
"Illuminate\\Log\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "11.x-dev"
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}