众所周知,新浪SAE是一家很好很强大的云平台,而ThinkPHP是一个对sae支持很好的,易于上手的框架,然而我研究了ThinkPHP中sae模式的日志实现,发现了一些问题,所以写出来分享一下。下面以ThinkPHP3.2.3版举例,但(截至本文发布前)ThinkPHP5版也有相同的问题。
话不多说,先上代码。ThinkPHP在sae模式下输出日志的实现是这样的:
(ThinkPHP3.2.3版,ThinkPHP\Library\Think\Log\Driver\Sae.class.php
)
/**
* 日志写入接口
* @access public
* @param string $log 日志信息
* @param string $destination 写入目标
* @return void
*/
public function write($log,$destination='') {
static $is_debug=null;
$now = date($this->config['log_time_format']);
$logstr="[{$now}] ".$_SERVER['REMOTE_ADDR'].' '.$_SERVER['REQUEST_URI']."\r\n{$log}\r\n";
if(is_null($is_debug)){
preg_replace('@(\w+)\=([^;]*)@e', '$appSettings[\'\\1\']="\\2";', $_SERVER['HTTP_APPCOOKIE']);
$is_debug = in_array($_SERVER['HTTP_APPVERSION'], explode(',', $appSettings['debug'])) ? true : false;
}
if($is_debug){
sae_set_display_errors(false);//记录日志不将日志打印出来
}
sae_debug($logstr);
if($is_debug){
sae_set_display_errors(true);
}
}
那么这样的代码输出的日志是什么样的呢?我们打开sae控制台-应用日志中心-错误日志,看到的是这种场景:
我们可以看到,这种情况下输出的日志是非常乱的。问题出在哪里呢?我来说明一下:
首先,所有在Log::record()
中记录的日志内容,会汇总到一个字符串,最后传给write方法,也就是上文中的代码。通过这个代码我们可以发现问题:
- 第一个问题,就是sae日志中心对单行日志的长度有限制。我们看上图,日志到了最后一个”第“的位置就截止了,后面的内容都没了。
- 第二个问题,就是上图中橙色圈中的#015#012。这个是什么呢?观察一下就会发现,这个就是代码中的
\r\n
。sae日志中心并不支持包含换行符等特殊字符的日志内容,换行只会显示为这个#015#012,令人感到莫名其妙。实际是这样的,SAE这边的日志,都是通过syslog协议传输的,然而syslog协议本身,也是用\n
去分割单条日志的,因此,在传输过程中,就需要进行转义了,在rsyslog的配置中,就会直接转义成#xxx的形式,就导致了这种情况。 - 还有一个小问题,就是上图中蓝色的圈,我们可以看到访问时间和IP被输出了两遍。这是因为sae日志中心默认就会记录访问时间和IP,而ThinkPHP又再次输出了一遍,导致重复。
针对以上问题,我建议用到这个sae日志功能的朋友对代码进行修改:
首先是重复问题,
$now = date($this->config['log_time_format']);
$logstr="[{$now}] ".$_SERVER['REMOTE_ADDR'].' '.$_SERVER['REQUEST_URI']."\r\n{$log}\r\n";
这两行代码改一下,把时间和IP去掉:
$logstr="REQUEST".$_SERVER['REQUEST_URI']."\r\n{$log}";
这样就避免了重复输出时间和IP。
然后是前两个问题,一个简单的改法是:
把
sae_debug($logstr);
改为
$arr = explode("\r\n", $logstr);
foreach ($arr as $s) {
sae_debug($s);
}
由于sae对单个请求支持多达1000行日志,所以这样把单行日志拆成多行,既解决了单行长度限制问题,顺便也把不能正常显示的换行符去掉了。
改好之后的效果:
可以看到这样一来整齐了很多。
不过这样还不够。再接下来,我要对sae日志中心进行魔改。先上效果图:
这就是最终的魔改效果。
我是怎么做的呢?分两步。第一步,再次修改write方法,为输出的日志添加花边:
$arr = explode("\r\n", $log);
sae_debug("╚════════════════════════════════════════════");
sae_debug('║ REQUEST ['.$_SERVER['REQUEST_URI']."]");
sae_debug("╟────────────────────────────────────────────");
foreach ($arr as $s) {
if(!empty(trim($s)))
sae_debug("║ ".$s);
}
sae_debug("╔════════════════════════════════════════════");
第二步,调整网页的css显示效果。我用的是Chrome插件Stylebot。
tbody.showLog.table {
background-color: #fdf6e3;
}
tbody.showLog.table tr td {
border-top: 0px;
padding: 0;
}
tr td img {
display: none;
}
tr td span {
color: #000000;
font-size: 16px;
}
tr td span[title="客户端IP"] {
color: #b58900;
}
tr td span[title="时区"] {
color: #008080;
}
tr td span[title="本条日志产生的时刻"] {
color: #008080;
}
这其中的几点:
- 一点是去掉日志前面的+号按钮,因为debug日志是并没有LogDetail的,前面有个加号按钮会让人总以为能展开出什么东西,但实际上什么都没有,实在是太让人难受了,所以一定要去掉。
- 然后就是既然展开不出什么别的东西,那么原有的日志行之间间距就太大了,缩小间距,去掉分割线,也便于展示花边效果。
- 最后就是配色了,原网页的IP、时间和内容明明有不同的title,显示字体颜色却完全一样,真是浪费。这里我用了类似Solarized Light风格的配色,大家也可以使用自己喜欢的风格。
这里将Stylebot的网址匹配设为sae.sina.com.cn/?service=http&m=applog&level=debug**
。注意这种魔改只适用于debug日志,不适用于访问日志(因为访问日志有LogDetail,内容也不能自定义),所以level=debug是要有的。
如此一来魔改就完成了。
魔改后会出现的另一个问题是,日志中心显示的日志是从后往前的,最新的在最上面,而如果下载日志查看的话却是从前往后的,这样一来两种模式的╚╔花边是反的。如果经常需要下载日志查看的话,可以选择不用这种花边。
以上。