日志

    hyperf-skeleton 项目内默认提供了一些日志配置,默认情况下,日志的配置文件为 config/autoload/logger.php ,示例如下:

    1. <?php
    2. return [
    3. 'default' => [
    4. 'handler' => [
    5. 'class' => \Monolog\Handler\StreamHandler::class,
    6. 'constructor' => [
    7. 'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
    8. 'level' => \Monolog\Logger::DEBUG,
    9. ],
    10. ],
    11. 'formatter' => [
    12. 'class' => \Monolog\Formatter\LineFormatter::class,
    13. 'constructor' => [
    14. 'format' => null,
    15. 'dateFormat' => null,
    16. 'allowInlineLineBreaks' => true,
    17. ]
    18. ],
    19. ],
    20. ];
    1. use Monolog\Formatter\LineFormatter;
    2. use Monolog\Handler\FirePHPHandler;
    3. use Monolog\Handler\StreamHandler;
    4. use Monolog\Logger;
    5. // 创建一个 Channel,参数 log 即为 Channel 的名字
    6. $log = new Logger('log');
    7. // 创建两个 Handler,对应变量 $stream 和 $fire
    8. $stream = new StreamHandler('test.log', Logger::WARNING);
    9. $fire = new FirePHPHandler();
    10. $dateFormat = "Y n j, g:i a";
    11. // 定义日志格式为 "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
    12. $output = "%datetime%||%channel||%level_name%||%message%||%context%||%extra%\n";
    13. // 根据 时间格式 和 日志格式,创建一个 Formatter
    14. $formatter = new LineFormatter($output, $dateFormat);
    15. // 将 Formatter 设置到 Handler 里面
    16. $stream->setFormatter($formatter);
    17. // 讲 Handler 推入到 Channel 的 Handler 队列内
    18. $log->pushHandler($stream);
    19. $log->pushHandler($fire);
    20. $log2 = $log->withName('log2');
    21. // add records to the log
    22. $log->warning('Foo');
    23. // add extra data to record
    24. // 1. log context
    25. $log->error('a new user', ['username' => 'daydaygo']);
    26. // 2. processor
    27. $log->pushProcessor(function ($record) {
    28. $record['extra']['dummy'] = 'hello';
    29. return $record;
    30. });
    31. $log->pushProcessor(new \Monolog\Processor\MemoryPeakUsageProcessor());
    32. $log->alert('czl');
    • 首先, 实例化一个 Logger, 取个名字, 名字对应的就是 channel
    • 可以为 Logger 绑定多个 Handler, Logger 打日志, 交由 Handler 来处理
    • Handler 可以指定需要处理那些 日志级别 的日志, 比如 Logger::WARNING, 只处理日志级别 >=Logger::WARNING 的日志
    • 谁来格式化日志? Formatter, 设置好 Formatter 并绑定到相应的 Handler
    • 日志包含那些部分: "%datetime%||%channel||%level_name%||%message%||%context%||%extra%\n"
    • 区分一下日志中添加的额外信息 contextextra: context 由用户打日志时额外指定, 更加灵活; extra 由绑定到 Logger 上的 Processor 固定添加, 比较适合收集一些 常见信息

    可能有些时候您更想保持大多数框架使用日志的习惯,那么您可以在 App 下创建一个 Log 类,并通过 __callStatic 魔术方法静态方法调用实现对 Logger 的取用以及各个等级的日志记录,我们通过代码来演示一下:

    框架组件所输出的日志在默认情况下是由 Hyperf\Contract\StdoutLoggerInterface 接口的实现类 Hyperf\Framework\Logger\StdoutLogger 提供支持的,该实现类只是为了将相关的信息通过 print_r() 输出在 标准输出(stdout),即为启动 Hyperf终端(Terminal) 上,也就意味着其实并没有使用到 monolog 的,那么如果想要使用 monolog 来保持一致要怎么处理呢?

    • 首先, 实现一个 StdoutLoggerFactory 类,关于 Factory 的用法可在 依赖注入 章节获得更多详细的说明。
    1. <?php
    2. declare(strict_types=1);
    3. class StdoutLoggerFactory
    4. {
    5. public function __invoke(ContainerInterface $container)
    6. {
    7. return Log::get('sys');
    8. }
    9. }
    • 申明依赖, 使用 StdoutLoggerInterface 的地方, 由实际依赖的 StdoutLoggerFactory 实例化的类来完成

    上面这么多的使用, 都还只在 monolog 中的 Logger 这里打转, 这里来看看 HandlerFormatter

    1. // config/autoload/logger.php
    2. $appEnv = env('APP_ENV', 'dev');
    3. if ($appEnv == 'dev') {
    4. $formatter = [
    5. 'class' => \Monolog\Formatter\LineFormatter::class,
    6. 'constructor' => [
    7. 'format' => "||%datetime%||%channel%||%level_name%||%message%||%context%||%extra%\n",
    8. 'allowInlineLineBreaks' => true,
    9. 'includeStacktraces' => true,
    10. ],
    11. ];
    12. } else {
    13. $formatter = [
    14. 'class' => \Monolog\Formatter\JsonFormatter::class,
    15. 'constructor' => [],
    16. ];
    17. }
    18. return [
    19. 'default' => [
    20. 'handler' => [
    21. 'class' => \Monolog\Handler\StreamHandler::class,
    22. 'constructor' => [
    23. 'stream' => 'php://stdout',
    24. 'level' => \Monolog\Logger::INFO,
    25. ],
    26. ],
    27. 'formatter' => $formatter,
    28. ],
    29. ]
    • 默认配置了名为 defaultHandler, 并包含了此 Handler 及其 Formatter 的信息
    • 获取 Logger 时, 如果没有指定 Handler, 底层会自动把 default 这一 Handler 绑定到 Logger
    • 非 dev 环境: 日志使用 JsonFormatter, 会被格式为 json, 方便投递到第三方日志服务