照猫画虎 实现 min-laravel 框架系列之日志

php   laravel  

路由

laravel 系统的日志是建立在 monolog包上的

加入依赖

  • 在 minlaravelframework/framework 的 composer.json 配置文件中添加依赖包

    1. "require": {
    2. ....
    3. "monolog/monolog": "^2.1",
    4. ....
    5. },
  • 在 minlaravel 目录下更新, composer update

日志文件加载过程

日志写入服务是作为基础服务提供者,在 application 初始化时就注册加载的服务

  1. Illuminate\Foundation\Application
  2. protected function registerBaseServiceProviders()
  3. {
  4. ...
  5. $this->register(new RoutingServiceProvider($this));
  6. ...
  7. }
  8. Illuminate\Log\LogServiceProvider 服务提供者类
  9. use Illuminate\Support\ServiceProvider;
  10. class LogServiceProvider extends ServiceProvider
  11. {
  12. /**
  13. * Register the service provider.
  14. *
  15. * @return void
  16. */
  17. public function register()
  18. {
  19. $this->app->singleton('log', function ($app) {
  20. return new LogManager($app);
  21. });
  22. }
  23. }

可以看出,为系统提供日志服务的类是 LogManager

LogManager 类

LogManager 类实现了 Psr\Log\LoggerInterface 接口函数,而 LoggerInterface 是monolog/monolog 提供的契约类,定义了一些日志函数,ex:error、warning、notice、info 等等

driver 方法

获取日志的具体实现,由 getDefaultDriver 方法可以看出,日志配置为 app/logging.php 文件中的 default 指定,系统默认是 stack,一般常用的有 single、daily

  1. public function driver($driver = null)
  2. {
  3. return $this->get($driver ?? $this->getDefaultDriver());
  4. }
  5. public function getDefaultDriver()
  6. {
  7. return $this->app['config']['logging.default'];
  8. }
  9. /**
  10. * Attempt to get the log from the local cache.
  11. *
  12. * @param string $name
  13. * @return \Psr\Log\LoggerInterface
  14. */
  15. protected function get($name)
  16. {
  17. // channels 为内存缓存,一次请求只 new 一次就行了,同时可以清楚的了解,Logger 类是 laravel 系统在 monolog 之上的自定义实现类
  18. try {
  19. return $this->channels[$name] ?? with($this->resolve($name), function ($logger) use ($name) {
  20. return $this->channels[$name] = $this->tap($name, new Logger($logger, $this->app['events']));
  21. });
  22. } catch (Throwable $e) {
  23. return tap($this->createEmergencyLogger(), function ($logger) use ($e) {
  24. $logger->emergency('Unable to create configured logger. Using emergency logger.', [
  25. 'exception' => $e,
  26. ]);
  27. });
  28. }
  29. }

resolve 方法

通过 name 参数,构造对应的函数进行调用,ex:stack => createStackDriver,daily => createDailyDriver

  1. protected function resolve($name)
  2. {
  3. // 读取配置文件的数据
  4. $config = $this->configurationFor($name);
  5. // 读取失败,抛出异常
  6. if (is_null($config)) {
  7. throw new InvalidArgumentException("Log [{$name}] is not defined.");
  8. }
  9. // 自定义实现
  10. if (isset($this->customCreators[$config['driver']])) {
  11. return $this->callCustomCreator($config);
  12. }
  13. // 拼接函数名称
  14. $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
  15. if (method_exists($this, $driverMethod)) {
  16. return $this->{$driverMethod}($config);
  17. }
  18. throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
  19. }

configurationFor 读取配置

  1. 配置文件值
  2. 'channels' => [
  3. 'stack' => [
  4. 'driver' => 'stack',
  5. 'channels' => ['single'],
  6. 'ignore_exceptions' => false,
  7. ],
  8. 'single' => [
  9. 'driver' => 'single',
  10. 'path' => storage_path('logs/laravel.log'),
  11. 'level' => 'debug',
  12. ],
  13. 'daily' => [
  14. 'driver' => 'daily',
  15. 'path' => storage_path('logs/laravel.log'),
  16. 'level' => 'debug',
  17. 'days' => 14,
  18. ],
  19. ......
  20. ]
  21. protected function configurationFor($name)
  22. {
  23. return $this->app['config']["logging.channels.{$name}"];
  24. }

createXxxDriver 初始化 monolog 类

  1. protected function createSingleDriver(array $config)
  2. {
  3. return new Monolog($this->parseChannel($config), [
  4. $this->prepareHandler(
  5. new StreamHandler(
  6. $config['path'], $this->level($config),
  7. $config['bubble'] ?? true, $config['permission'] ?? null, $config['locking'] ?? false
  8. ), $config
  9. ),
  10. ]);
  11. }
  12. protected function createDailyDriver(array $config)
  13. {
  14. return new Monolog($this->parseChannel($config), [
  15. $this->prepareHandler(new RotatingFileHandler(
  16. $config['path'], $config['days'] ?? 7, $this->level($config),
  17. $config['bubble'] ?? true, $config['permission'] ?? null, $config['locking'] ?? false
  18. ), $config),
  19. ]);
  20. }

Logger 类

laravel 自定义的日志实现类,此类也实现了 LoggerInterface 接口类,

  1. // 写日志
  2. protected function writeLog($level, $message, $context)
  3. {
  4. $this->logger->{$level}($message = $this->formatMessage($message), $context);
  5. // 事件相关实现
  6. $this->fireLogEvent($level, $message, $context);
  7. }
  8. // 格式化信息,数组、json、等进行字符串转化
  9. protected function formatMessage($message)
  10. {
  11. if (is_array($message)) {
  12. return var_export($message, true);
  13. } elseif ($message instanceof Jsonable) {
  14. return $message->toJson();
  15. } elseif ($message instanceof Arrayable) {
  16. return var_export($message->toArray(), true);
  17. }
  18. return $message;
  19. }

Facades 实现

  • config/app.php 文件中的别名

    1. // Facades 用到的别名问题
    2. 'aliases' => [
    3. ...
    4. 'Log' => Illuminate\Support\Facades\Log::class,
    5. ...
    6. ],
  • Facades 类

    1. namespace Illuminate\Support\Facades;
    2. class Log extends Facade
    3. {
    4. /**
    5. * Get the registered name of the component.
    6. *
    7. * @return string
    8. */
    9. protected static function getFacadeAccessor()
    10. {
    11. return 'log';
    12. }
    13. }

测试

public/app.php 文件中,

  1. // 日志
  2. $log = $app['log'];
  3. $log->log('error',"日志",[]);
  4. $log->info("日志");
  5. \Log::debug("日志");


评论 0

发表评论

Top