1. 问题

由于项目有大量的需要和外部对接的接口,所以为了程序员自身安全起见,需要对输入输出做日志记录。项目的基本情况:

  1. 使用yii2项目
  2. 项目的输出已经使用了标准输出类处理了
  3. 对输入输出的日志记录要能对应上(因为有并发问题,插入日志的顺序会打乱)

输出已经标准化处理的意思如下:

  1. <?php
  2. class Out {
  3. /** 成功输出 $data(返回数据),$msg(返回消息) */
  4. public static function success($data = '', $msg = null) {
  5. $code = self::$_success;
  6. $msg = $msg ?: self::getMsg($code);
  7. self::manual($code, $data, $msg);
  8. }
  9. /** 执行出错 $msg(返回消息) */
  10. public static function fail($msg = null,$data = '') {
  11. $code = self::$_fail;
  12. $msg = $msg ?: self::getMsg($code);
  13. self::manual($code, $data, $msg);
  14. }
  15. /** 自定义返回 $msg(返回消息) */
  16. public static function manual($code, $data = '', $msg = null) {
  17. exit(Json::encode(array(
  18. 'code' => $code,
  19. 'data' => $data,
  20. 'msg' => $msg
  21. )));
  22. }
  23. }
<?php
public function actionRespond() {
    try {
        // 请求处理
                dosomething();

        Out::success([], "成功");
    } catch (\Exception $e) {
        Out::fail($e->getMessage());
    }
}

2. 处理过程

  1. 查看源代码: ```php <? // 入口文件的最后一行代码 (new yii\web\Application($config))->run();

// run方法的 // vendor/yiisoft/yii2/base/Application.php public function run() { try { $this->state = self::STATE_BEFORE_REQUEST; $this->trigger(self::EVENT_BEFORE_REQUEST);

    $this->state = self::STATE_HANDLING_REQUEST;
    $response = $this->handleRequest($this->getRequest());

    $this->state = self::STATE_AFTER_REQUEST;
    $this->trigger(self::EVENT_AFTER_REQUEST);

    $this->state = self::STATE_SENDING_RESPONSE;
    $response->send();

    $this->state = self::STATE_END;

    return $response->exitStatus;
} catch (ExitException $e) {
    $this->end($e->statusCode, isset($response) ? $response : null);
    return $e->statusCode;
}

}

可以看到yii2框架处理请求的输入输出其实在一行代码来实现:<br />`$response = $this->handleRequest($this->getRequest());`<br />而且请求前后有触发器实现请求前后的处理:<br />`$this->trigger(self::EVENT_BEFORE_REQUEST);`<br />`$this->trigger(self::EVENT_AFTER_REQUEST);`<br />因此很明显,只需要在请求前后做处理就好了,yii2本身可以通过事件Event来实现。

2. 动手写事件:
```php
<?php
namespace app\component\events\controller;

use common\extend\Log;
use common\extend\Request;
use common\extend\String;
use Yii;
use yii\base\Event;

class EventRequest extends Event {

    // 日志
    private $log;

    // 请求的唯一id
    private static $requestId;

    public function __construct(array $config = []) {
        $this->log = new Log("app");

        self::$requestId = String::getRandStr(20) . '-' . time();

        parent::__construct($config);
    }

    public function init(){
        Yii::$app->on(yii\base\Application::EVENT_BEFORE_REQUEST, function($event){
            $requestIp = Yii::$app->request->getUserIP();
            $u = Request::currentUrl();
            $p = json_encode(Request::getPost());
            $g = json_encode(Request::getGet());
            $request = sprintf("[ip:]%s\n[url:]%s\n[post:]%s\n[get:]%s", $requestIp, $u, $p, $g);

            $this->log->add('[Request:]' . '[id:' . self::$requestId . "]\n" . $request);
            $this->log->add('````````````````````````````````````````````````````````````````````````````');
        });

        Yii::$app->on(yii\base\Application::EVENT_AFTER_REQUEST, function($event){
            $data = ob_get_contents();
            $dd = Yii::$app->response->data;
            $this->log->add('[Return:]' . '[id:' . self::$requestId . "]\n" . $data . $dd);
            $this->log->add('````````````````````````````````````````````````````````````````````````````');
        });
    }

    /** 获取请求ID */
    public static function getRequestId() {
        return self::$requestId;
    }
}

配置:

<?
// 在main.php中配置加上事件处理类
'bootstrap' => ['\\wxapp\\component\\events\\controller\\EventRequest'],
  1. 写完代码,调试:

调试时,发现只有输入的日志,没有输出的日志,想起来因为项目代码改造成了没有按照yii2的原路去返回。因此要做兼容。

  1. 改造Out类: ```php <? /* 自定义返回 $msg(返回消息) / public static function manual($code, $data = ‘’, $msg = null) { self::outStr(Json::encode(array(
     'code' => $code,
     'data' => $data,
     'msg' => $msg
    
    ))); }

/**

  • 统一输出
  • @param string $string
  • @param \common\extend\Log|null $log */ public static function outStr(string $string, $log = null) {

    if ($log instanceof Log) {

     $log->add($string);
    

    } else{

     // 获取日志路径
     $p = pathinfo(\Yii::$app->getBasePath());
     $d = $p['basename'] ?? '';
    
     // 获取请求ID
     $id = EventRequest::getRequestId();
    
     $log = new Log($d);
     $log->add('[Respond From Out:]' . "[id:$id]" . "\n" . $string);
     $log->add('````````````````````````````````````````````````````````````````````````````');
    

    }

    exit($string); } ```

  1. 调试,正常,nice

    3. 总结

  • 使用到yii2自带的事件处理功能。