REST API,ThinkPHP 统一异常错误处理,日志记录
最近公司项目,前后端分离,为了统一rest分格的API,对异常处理统一规范。
如果是调试模式,走thinkphp框架的原本流程
如果是关闭调试,处理异常统一显示500错误信息,避免暴露敏感信息和友好提示并且统一json格式返回。
整体思路差不多,最近的项目5.0和3.2版本的都有,但是TP5.0和3.2版本还是有点小区别,所以都贴上代码
TP3.2 版本
注意:3.2版本,没有exception_handle
这个配置项,所以需要自己配置“自定义的异常处理函数”
设置使用的函数set_exception_handler
,不了解的同学自己查看手册。
因为我自己的接口都会继承BaseController,所以直接把设置写在这个类的构造函数中。
class BaseController extends Controller
{
public function __construct()
{
parent::__construct();
set_exception_handler('Lib\Exception\ExceptionHandler::render');
}
}
<?php
namespace Lib\Exception;
use Exception;
use think\exception\Handle;
use Think\Log;
use Think\Think;
class ExceptionHandler
{
private static $code;
private static $msg;
private static $errorCode;
public static function render(Exception $e)
{
// 如果是自定义异常
if ($e instanceof BaseApiException){
self::$code = $e->code;
self::$msg = $e->msg;
self::$errorCode = $e->errorCode;
}else{
// 非调试模式,不要暴露具体错误,只写日志
if (APP_DEBUG == false){
self::$code = 500;
self::$msg = '服务器内部错误';
self::$errorCode = 999;
self::recordErrorLog($e);
}else{
Think::appException($e);
}
}
$result = [
'msg' => self::$msg,
'error_code' => self::$errorCode,
'request_url' => __SELF__
];
// 3.2版本没发现什么返回好用的函数,这里ajaxReturn是自己写的函数,第二个参数是http code
ajaxReturn($result, self::$code);
}
private static function recordErrorLog(Exception $e)
{
Log::write($e->getMessage(), 'error', 'File');
}
}
对比5.0版本的代码,3.2版本记录日志前并没有初始化,因为3.2版本日志记录功能太简单了。
3.2版本指定的日志级别记录,是通过全局配置的C('LOG_LEVEL')
。
Log.init 方法并没有提供level参数来指定记录的级别。
源代码文件 Think\Log.php
static function record($message,$level=self::ERR,$record=false) {
if($record || false !== strpos(C('LOG_LEVEL'),$level)) {
self::$log[] = "{$level}: {$message}\r\n";
}
}
TP5.0版本
5.0版本,官方宣传 —— 为API开发而设计的高性能框架,的确这个口号正如他的表现一样。
自定义的异常处理函数,只要在配置文件配置即可
// 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle' => 'app\lib\exception\ExceptionHandler',
<?php
namespace app\lib\exception;
use Exception;
use think\exception\Handle;
use think\Log;
use think\Request;
class ExceptionHandler extends Handle
{
private $code;
private $msg;
private $errorCode;
public function render(Exception $e)
{
if ($e instanceof BaseException){
// 如果是自定义异常
$this->code = $e->code;
$this->msg = $e->msg;
$this->errorCode = $e->errorCode;
}else{
if (config('app_debug')){
return parent::render($e);
}else{
$this->code = 500;
$this->msg = '服务器内部错误';
$this->errorCode = 999;
$this->recordErrorLog($e);
}
}
$request = Request::instance();
$result = [
'msg' => $this->msg,
'error_code' => $this->errorCode,
'request_url' => $request->url()
];
// 5.0版本自带的json函数,更方便
return json($result, $this->code);
}
private function recordErrorLog(Exception $e){
Log::init([
// 日志记录方式,内置 file socket 支持扩展
'type' => 'file',
// 日志保存目录
'path' => LOG_PATH,
// 日志记录级别, 这里只有error级别以上的才会记录
'level' => ['error'],
]);
Log::record($e->getMessage(), 'error');
}
}
相比3.2版本,新版日志记录功能更强大,可以指定错误级别来记录错误日志。
源代码文件 Think\Log.php
<?php
/**
* 保存调试信息
* @access public
* @return bool
*/
public static function save()
{
......
if (empty(self::$config['level'])) {
// 获取全部日志
$log = self::$log;
if (!App::$debug && isset($log['debug'])) {
unset($log['debug']);
}
} else {
// 记录允许级别
$log = [];
foreach (self::$config['level'] as $level) {
if (isset(self::$log[$level])) {
$log[$level] = self::$log[$level];
}
}
}
......
}