zoukankan      html  css  js  c++  java
  • php 日志模块源码解析

    php日志模块设计

    Monolog 是PHP的一个日志类库解析

     整体介绍:monolog日志模块遵循 PSR3 的接口规范。主要有日志格式类接口(格式化日志信息),处理类接口(写日志的驱动,通过扩展写入不同地方),过程类接口(在有机组合,利于扩展和兼容。

    handler的level值决定其处理大于的等级日志,代码参考AbstractHandler里面的isHandling是否需要处理

    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就可以。

    关键的代码

    // 抽象类的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 MonologLogger;
    
    use MonologHandlerStreamHandler;
    
    use MonologHandlerErrorLogHandler;
    
    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

  • 相关阅读:
    node版本切换工具n的使用介绍
    nrm -- npm镜像源管理
    npm vs npx
    Dnsmasq MacOS使用介绍
    Java动态生成类以及动态添加属性
    mysql表名忽略大小写问题记录
    psotgresql之大小写
    java 按字节读写二进制文件(Base64编码解码)
    java解析复杂json:JSONObject 和 JSONArray的使用
    URL编码:怎样读取特殊字符
  • 原文地址:https://www.cnblogs.com/swing07/p/10222737.html
Copyright © 2011-2022 走看看