1. 问题
由于项目有大量的需要和外部对接的接口,所以为了程序员自身安全起见,需要对输入输出做日志记录。项目的基本情况:
- 使用yii2项目
- 项目的输出已经使用了标准输出类处理了
- 对输入输出的日志记录要能对应上(因为有并发问题,插入日志的顺序会打乱)
输出已经标准化处理的意思如下:
<?phpclass Out {/** 成功输出 $data(返回数据),$msg(返回消息) */public static function success($data = '', $msg = null) {$code = self::$_success;$msg = $msg ?: self::getMsg($code);self::manual($code, $data, $msg);}/** 执行出错 $msg(返回消息) */public static function fail($msg = null,$data = '') {$code = self::$_fail;$msg = $msg ?: self::getMsg($code);self::manual($code, $data, $msg);}/** 自定义返回 $msg(返回消息) */public static function manual($code, $data = '', $msg = null) {exit(Json::encode(array('code' => $code,'data' => $data,'msg' => $msg)));}}
<?php
public function actionRespond() {
try {
// 请求处理
dosomething();
Out::success([], "成功");
} catch (\Exception $e) {
Out::fail($e->getMessage());
}
}
2. 处理过程
- 查看源代码: ```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'],
- 写完代码,调试:
调试时,发现只有输入的日志,没有输出的日志,想起来因为项目代码改造成了没有按照yii2的原路去返回。因此要做兼容。
- 改造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); } ```
- 使用到yii2自带的事件处理功能。
