背景

由于各种原因,没有接入完整的调用链追踪,(┬_┬)。但是我们自身再通过php的curl调用各端接口时,会请求多次。那么有没有一种方法可以在不植入业务代码的前提下,捕捉到这些curl的请求呢。显然,ci有基本的hook操作。我们可以在相关节点时,可选择的把这些收集到到的通过异步的方式发送给指定的监听者。

curl类库(部分代码)

class Ycurl{    public $resource_arr;    public static $resource_id = 0; //资源(resource handle id    public $save_requests = true;//是否保存,默认全部保存    public $requests = array();//n次请求参数、返回参数,错误(如果有)    public $request_counts = 0;//总的请求次数,可能一个页面调用多次    public $send_redis_email = false;//是否需要以异常方式发送邮件    ...       $ret = curl_exec($ch);    $curl_info_arr = curl_getinfo($ch);    $this->request_counts += 1;    if ($this->save_requests === true) {        $arg_list = func_get_args();        $this->requests[$resource_id]['url'] = $curl_info_arr['url'];        $this->requests[$resource_id]['req_params'] = json_encode([$arg_list], 320);        $this->requests[$resource_id]['response'] = $ret;        $this->requests[$resource_id]['http_code'] = $curl_info_arr['http_code'];        $this->requests[$resource_id]['is_error'] = curl_error($ch);        $this->requests[$resource_id]['total_time'] = $curl_info_arr['total_time'];        $this->requests[$resource_id]['primary_ip'] = $curl_info_arr['primary_ip'];        if($this->send_redis_email){            redis_list_add(json_encode($this->requests,320));        }    }

hooks config

从ci对象中抽取curl对象,其他自定义的对象也可以同样思路。

$hook['post_system'][] = array(    'class'    => 'Curl_trace',    'function' => '_split_ci_of_curl',    'filename' => 'curl_trace.php',    'filepath' => 'hooks'

curl_trace.php

<?php/** * Created by PhpStorm. * User: TongBo * Date: 2019/2/26 * Time: 16:19 */defined('BASEPATH') OR exit('No direct script access allowed');Class Curl_trace{    public $ci;    public function __construct()    {        $this->ci =& get_instance();    }    /**     *抽取整个ci对象的所有curl对象实例     * @time 2019/3/1 11:12     * @author tongbo     */    public function _split_ci_of_curl()    {        $class = $this->ci->router->fetch_class();        $need_trace_controller = [            'home',            'admin',        ];        $func = $this->ci->router->fetch_method();        if (in_array($class, $need_trace_controller) or 1) {            foreach (get_object_vars($this->ci) as $name => $ci_object) {                if (is_object($ci_object)) {                    if ($ci_object instanceof Ycurl) {                        $curls[get_class($this->ci) . "/{$func}:$" . $name] = $ci_object;                    }                }            }            if (!empty($curls)) {                foreach ($curls as $ctl_func => $curl) {                    foreach ($curl->requests as $index => $single_curl_obj) {                        $tmp[$ctl_func][$index] = $single_curl_obj;                        $tmp[$ctl_func][$index]['record_time'] = date('Ymd H:i:s' . substr((string)microtime(), 1, 8) . ' e');                        redis_list_add(json_encode([$ctl_func . "_" . $index, $tmp[$ctl_func][$index]], 320));                    }                }            }        }    }}

队列中的结果

不足

ci框架中不可避免的使用了exit操作,或者其他业务代码中也使用。这样导致钩子函数无法正常完成他的使用。除了必要规范不必要的exit之外,我们也可以用register_shut_down这个函数来实现每次都必须记录。(考虑性价比吧)