照猫画虎 实现 min-laravel 框架系列之日志
- laravel
- 2020-08-26
- 5111
- 0
路由
laravel 系统的日志是建立在 monolog
包上的
加入依赖
在 minlaravelframework/framework 的 composer.json 配置文件中添加依赖包
"require": {
....
"monolog/monolog": "^2.1",
....
},
在 minlaravel 目录下更新, composer update
日志文件加载过程
日志写入服务是作为基础服务提供者,在 application 初始化时就注册加载的服务
Illuminate\Foundation\Application 类
protected function registerBaseServiceProviders()
{
...
$this->register(new RoutingServiceProvider($this));
...
}
Illuminate\Log\LogServiceProvider 服务提供者类
use Illuminate\Support\ServiceProvider;
class LogServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('log', function ($app) {
return new LogManager($app);
});
}
}
可以看出,为系统提供日志服务的类是 LogManager
LogManager 类
LogManager 类实现了 Psr\Log\LoggerInterface 接口函数,而 LoggerInterface 是monolog/monolog
提供的契约类,定义了一些日志函数,ex:error、warning、notice、info 等等
driver 方法
获取日志的具体实现,由 getDefaultDriver 方法可以看出,日志配置为 app/logging.php 文件中的 default 指定,系统默认是 stack,一般常用的有 single、daily
public function driver($driver = null)
{
return $this->get($driver ?? $this->getDefaultDriver());
}
public function getDefaultDriver()
{
return $this->app['config']['logging.default'];
}
/**
* Attempt to get the log from the local cache.
*
* @param string $name
* @return \Psr\Log\LoggerInterface
*/
protected function get($name)
{
// channels 为内存缓存,一次请求只 new 一次就行了,同时可以清楚的了解,Logger 类是 laravel 系统在 monolog 之上的自定义实现类
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,
]);
});
}
}
resolve 方法
通过 name 参数,构造对应的函数进行调用,ex:stack => createStackDriver,daily => createDailyDriver
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.");
}
configurationFor 读取配置
配置文件值
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single'],
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'days' => 14,
],
......
]
protected function configurationFor($name)
{
return $this->app['config']["logging.channels.{$name}"];
}
createXxxDriver 初始化 monolog 类
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
),
]);
}
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),
]);
}
Logger 类
laravel 自定义的日志实现类,此类也实现了 LoggerInterface 接口类,
// 写日志
protected function writeLog($level, $message, $context)
{
$this->logger->{$level}($message = $this->formatMessage($message), $context);
// 事件相关实现
$this->fireLogEvent($level, $message, $context);
}
// 格式化信息,数组、json、等进行字符串转化
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 $message;
}
Facades 实现
config/app.php 文件中的别名
// Facades 用到的别名问题
'aliases' => [
...
'Log' => Illuminate\Support\Facades\Log::class,
...
],
Facades 类
namespace Illuminate\Support\Facades;
class Log extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'log';
}
}
测试
public/app.php 文件中,
// 日志
$log = $app['log'];
$log->log('error',"日志",[]);
$log->info("日志");
\Log::debug("日志");