程序报错

  • 作者:KK

  • 发表日期:2016.8.18


注意:请先阅读业务报错了解一下先再读本文


反之,如果客户端的参数都通过了服务端的校验,然后服务端自己程序的过程中居然出了问题,这才是程序错误,此时需要进行日志记录

$title = trim((string)$this->post('title'));
$titleLength = mb_strlen($title, 'UTF-8');
if($titleLength < 6 || $titleLength > 20){
	$this->ajaxReturn('标题长度应为6到20个字符');
}
$row = $db->table('article')->insert(['title' => $title])->insert();
if(!$row){
	throw new Exception('添加文章失败,请联系网站管理人员'); //通常大部分框架在抛出异常时能触发日志,不管抛异常还是干嘛,反正要既报错又要有日志
	//$this->ajaxReturn('添加文章失败,请联系网站管理人员'); //例如是TP框架的话不要用这个,因为没有日志
}
$this->ajaxReturn('添加成功');

以上在insert失败时我的演示代码就通过抛出异常来报错并且触发框架记录日志,这样程序员就能发现问题并去排查了

为什么要记录日志?因为用户都按照约定给了文章标题,用户已经没做错什么了,接下来是服务器入库时不知出了什么错而导致的失败,所以这要界定为服务器程序出错了,不止要报错+记录错误信息到日志中,还要返回500状态码给客户端才对


实现程序报错支持

如果直接抛出异常,那很多框架自然会记录异常到日志中,并在网页上显示相关错误内容,但有一个需求就是我们都不希望大部分程序报错都显示到网页上,给用户看到这结莫名奇妙的路径,Error符号和相关的dump信息;还有一个原因就是不希望将网站程序的服务器相关路径和服务器信息等暴露到外面。像这样的代码:

throw new ErrorException('读取XX表的信息失败|缓存初始化失败');

然后就会输出下面这样一堆信息:

exception 'ErrorException' with message '读取XX表的信息失败|缓存初始化失败' in xxx\Lib\Action\Home\MessageBoardAction.class.php:30
Stack trace:
#0 [internal function]: MessageBoardAction->showList()
#1 yyy\ThinkPHP\Lib\Core\App.class.php(171): ReflectionMethod->invoke(Object(MessageBoardAction))
#2 yyy\ThinkPHP\Lib\Core\App.class.php(207): App::exec()
#3 yyy\ThinkPHP\Lib\Core\Think.class.php(39): App::run()
#4 yyy\ThinkPHP\Common\runtime.php(242): Think::start()
#5 yyy\ThinkPHP\ThinkPHP.php(30): require('web\...')
#6 web\index.php(8): require('web\...')
#7 {main}

所以你想想用户看到会是啥感想?——不明觉厉!

然而程序出错时,作为开发者我们当然也希望看到报错信息,但又不被用户看到,我的思路就是通过set_exception_handler注册异常处理回调,在这个回调里将异常的内容记录到日志里,然后对请求客户端输出统一的模糊报错比如“抱歉,系统运行出了点问题,请稍后再试”

实现的代码思路大概如下:

//程序初始化的地方:
set_exception_handler(function($exception){
	$logFile = date('error-Y-m-d' . '.log');
	file_put_contents($logFile, $exception->__toString());
	$errorMessage = '抱歉,系统运行出了点问题,请稍后再试';
	if(ENV_DEV){ //线下时这个常量定义成true,线上时定义成false
		$errorMessage = $exception->__toString(); //这样就能在开发时看到报错内容了
	}
	exit('<span style="color:red;">' . $errorMessage . '</span>');
});

//非异常的错误也要统一为异常
set_error_handler(function($code, $message, $file, $line){
	throw new ErrorException($message, $code, $code, $file, $line); //抛出异常后会被set_exception_handler的回调处理
});

throw new ErrorException('测试错误');
//echo $不存在的变量;
//file_get_contents('不存在的用户头像或啥文件');