一个简易的debug库设计与实现
背景
最近项目上线了广告Offer按照ecpm等排序策略功能,上线之后经常被pm骚扰,因为他经常想查看为什么一个offer没有展示等问题,每次都要帮他查看线上日志,过程很痛苦,占用了大把时间。必须要改变这种现状。
debug的用途
便于线上case追踪用,分析程序执行的每个环节。
设计要点
- debug信息的层级关系
为了很好地阅读debug信息,必须将debug信息很好地组织起来,比如一个请求来了,在后台执行的时候需要经过好几步,stage1, stage2,state3,…,其中stage1中又有好几步,我们可以把这些信息按照树的结构组织起来:
{
"request": {
"ip": "180.92.201.3",
"uri": "/api/offer",
"network": "wifi",
"debugid": "8782399662"
},
"process": {
"stage_readOffers": [
{
"offer_id": "3142",
"type": "aio",
"flags": {
"d": 1,
"x": false,
"ne": -1
}
},
{
"offer_id": "3142",
"type": "aio",
"flags": {
"d": 1,
"x": false,
"ne": -1
}
},
...
],
"stage_filterOffers": [
{
"offer_id": "3142",
"type": "aio",
"flags": {
"d": 1,
"x": false,
"ne": -1
}
},
...
],
...
},
"response": {
"offer": {
"offer_id": "3142",
"type": "aio",
"flags": {
"d": 1,
"x": false,
"ne": -1
}
}
}
}
这样我们就知道每一步发生了什么,为什么有些offer信息没有展示,有些展示了,整个请求走了哪些逻辑分支。
- 如何动态地添加debug信息
打印debug信息是分散在代码的各个地方,如何方便地将这些分散的debug信息组织起来?如果是动态语言,这种操作很方便地,比如上面的json格式和python的dict和php的array是对应起来的。如果是C/C++这种静态语言,需要单独开发,公司的bsl::IVar就是做这样的事,有时间研究一下。
那具体怎么添加呢?比如我要给上面的process的stage2添加一个offer,可以这样做:
addDebug('process.stage2.offer', $offer);
public function addDebug($key, $info, $flag = false) {
$offset = 0;
$keyLen = strlen($key);
$key = trim($key, Mj_Debug::SEP_KEY_CHAR);
$cur = $this->value;
while ($found = strpos($key, Mj_Debug::SEP_KEY_CHAR, $offset)) {
$tmp = substr($key, $offset, $found - $offset);
if (!isset($cur[$tmp])) {
$cur[$tmp] = array();
}
$cur = $cur[$tmp];
$offset = $found + 1;
}
$lastKey = substr($key, $offset);
if (!isset($cur[$lastKey])) {
$cur[$lastKey] = array();
}
if ($flag) {
$cur[$lastKey] = $info;
} else {
$cur[$lastKey][] = $info;
}
}
这样在debug库的内部自动解析成数组的维度。
- 如何避免影响对主要业务逻辑的性能影响
因为一个请求来了之后可能打印很多的debug信息,而这些debug信息落地或者网络传输到cache中,由于数据量大,有两个弊端,一个是占用业务逻辑的大量cache空间,一个是影响主要逻辑的性能。为了避免这两点,可以引入旁路cache,这个cache和业务逻辑的cache不是同一个,这样就避免了影响业务逻辑。旁路cache的写入,往往需要单独的后台线程进行写入,这样就需要一个任务队列了,业务代码产生debug信息放到任务队列,后台线程读这个任务队列,将debug信息写入cache。
但是,对应php这种语言,没有线程这一说,就不存在旁路写入了,但是php使用的场景是nginx的一个请求来了,启动一个子进程,也就是说php处理请求地时候是在一个单独的进程中,这样对其他的请求不会影响。 而且实际使用中debug的请求只是站所有请求的很小一部分。而且将将debug信息输出,往往只是内存地操作,速度很快。
- 如何避免cache中的debug信息过多
这个好解决。首先并不是所有的请求都需要写debug信息,可以采用随机抽样的方式,比如只有5%的请求需要打debug信息,而且写入到cache中的debug信息是会失效的,可以设置超时。
代码实现
参见debuglib