php日志模块设计
Monolog 是PHP的一个日志类库解析
整体介绍:monolog日志模块遵循 PSR3 的接口规范。主要有日志格式类接口(格式化日志信息),处理类接口(写日志的驱动,通过扩展写入不同地方),过程类接口(在有机组合,利于扩展和兼容。
handler的level值决定其处理大于的等级日志,代码参考AbstractHandler里面的isHandling是否需要处理
1 2 3 4 | public function isHandling( array $record ) { return $record [ 'level' ] >= $this ->level; } |
1. Logger.php文件里面的Logger类 logger可以通过构建的时候或者调用 pushHandler 添加处理里处理日志的输出驱动 pushProcessor添加处理过程其作用是给日志格式添加数据(源码可以看看ProcessIdProcessor类)。
addRecord方法比较关键 里面能看到handler的调用 process的调用
2. process都实现ProcessorInterface 接口,其只有__invoke 用于类的函数调用,就是把类当做方法调用是默认嗲用的方法。如: call_user_func($processor, $record); $processor 类 $record传入的参数。扩展添加process时
3. handle 处理日志的写入地方 ,实现HandlerInterface接口 AbstractHandler是HandlerInterface接口的抽象类 实现了部分方法 AbstractProcessingHandler是基于AbstractHandler的抽象了 他主要是实现了handle默认的方法, 一般扩展基于AbstractProcessingHandler 实现write方法。 handle处理类里面也可以添加process,这里看似和logger也添加process有点重复 handle类可以通过setFormatter方法添加FormatterInterface格式接口 对日志信息格式化
4. 格式化NormalizerFormatter实现FormatterInterface接口 主要实现 format方法。class LineFormatter extends NormalizerFormatter 重写了format方法。 所以一般扩展格式化类继承NormalizerFormatter类,重写NormalizerFormatter就可以。
关键的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | // 抽象类的handle方法 abstract class AbstractProcessingHandler extends AbstractHandler { /** * {@inheritdoc} */ public function handle( array $record ) { if (! $this ->isHandling( $record )) { return false; } $record = $this ->processRecord( $record ); $record [ 'formatted' ] = $this ->getFormatter()->format( $record ); $this ->write( $record ); return false === $this ->bubble; } /** * Logger 类的 记录日志方法 里面调用了 process handle * Adds a log record. * * @param int $level The logging level * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addRecord( $level , $message , array $context = array ()) { if (! $this ->handlers) { $this ->pushHandler( new StreamHandler( 'php://stderr' , static ::DEBUG)); } $levelName = static ::getLevelName( $level ); // check if any handler will handle this message so we can return early and save cycles $handlerKey = null; reset( $this ->handlers); while ( $handler = current( $this ->handlers)) { if ( $handler ->isHandling( array ( 'level' => $level ))) { $handlerKey = key( $this ->handlers); break ; } next( $this ->handlers); } if (null === $handlerKey ) { return false; } if (! static :: $timezone ) { static :: $timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC' ); } // php7.1+ always has microseconds enabled, so we do not need this hack if ( $this ->microsecondTimestamps && PHP_VERSION_ID < 70100) { $ts = \DateTime::createFromFormat( 'U.u' , sprintf( '%.6F' , microtime(true)), static :: $timezone ); } else { $ts = new \DateTime(null, static :: $timezone ); } $ts ->setTimezone( static :: $timezone ); $record = array ( 'message' => (string) $message , 'context' => $context , 'level' => $level , 'level_name' => $levelName , 'channel' => $this ->name, 'datetime' => $ts , 'extra' => array (), ); try { foreach ( $this ->processors as $processor ) { $record = call_user_func( $processor , $record ); } while ( $handler = current( $this ->handlers)) { if (true === $handler ->handle( $record )) { break ; } next( $this ->handlers); } } catch (Exception $e ) { $this ->handleException( $e , $record ); } return true; } |
测试:
monolog 采用composer安装:
composer require monolog/monolog
composer install
php 测试代码
<?php use Monolog\Logger; use Monolog\Handler\StreamHandler; use Monolog\Handler\ErrorLogHandler; require 'vendor/autoload.php'; $logger = new Logger('my_logger'); $logger->pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG)); $logger->pushHandler(new ErrorLogHandler(ErrorLogHandler::OPERATING_SYSTEM, Logger::ERROR)); $logger->debug('行胜于言'); $logger->info('行胜于言'); $logger->notice('行胜于言'); $logger->error('行胜于言'); $logger->critical('行胜于言'); $logger->alert('行胜于言'); $logger->emergency('行胜于言'); ?>
使用
参考测试例子能看, 日志可以添加多个handle 注意handle后添加的先处理堆的方式。 记录的level大于设定的都会被相应的handle处理,
如new ErrorLogHandler(ErrorLogHandler::OPERATING_SYSTEM, Logger::ERROR) 处理错误等级以上的日志信息。$bubble决定是否冒泡给下一个handle处理 为false的时候 这个设定等级以上的日志被处理后其他handle将处理不到。
疑问
1. handler为什么是堆的方式,后加入的handler先处理日志。handler的bubble设置为false就会直接跳过后续handler的处理。
参考
文章详细介绍了monolog的各个模块的功能
https://blog.csdn.net/sanbingyutuoniao123/article/details/71079534
psr 规范的日志接口详细说面
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
文章介绍了 日志系统的整体结构
https://www.cnblogs.com/mawang/p/6740862.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步