render渲染方法重写:
1、如果是ajax请求,则返回对应状态,调试模式下返回堆栈
2、如果是页面请求,则返回对应错误模板页面渲染。
report报告方法重写:
1、Undefined index、offset、variable比较常见,屏蔽以不推送消息,采用原生记录即可
2、推送消息走异步,防止并发出现响应变慢。记录异常的前几个堆栈的参数,方便开发验证问题
3、推送异常,走默认原生记录,防止死循环
<?php
/*
* @Description: 应用异常处理类
* @Author: KingFer
* @Date: 2019-08-13 17:24:45
*/
namespace app;
use app\common\helper\QueueHelper;
use ErrorException;
use Exception;
use InvalidArgumentException;
use ParseError;
use PDOException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\ClassNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\RouteNotFoundException;
use think\exception\ValidateException;
use think\Response;
use Throwable;
use TypeError;
class ExceptionHandle extends Handle
{
/**
* 不需要记录信息(日志)的异常类列表
* @var array
*/
protected $ignoreReport = [
HttpResponseException::class,
ValidateException::class,
];
protected $error_num;
public function __construct()
{
$this->error_num = 'Y' . makeRandCode(13) . date('mdHis');
parent::__construct(app());
}
/**
* Render an exception into an HTTP response.
*
* @access public
* @param \think\Request $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
if (!$this->isIgnoreReport($e)) {
// 添加自定义异常处理机制
// 参数验证错误
if ($e instanceof ValidateException) {
return json($e->getError(), 422);
}
// ajax请求404异常 , 不返回错误页面
if (($e instanceof ClassNotFoundException || $e instanceof RouteNotFoundException) && request()->isAjax()) {
return json(['msg' => env('app_debug') ? $e->getMessage() : '当前请求资源不存在,请稍后再试', 'code' => 404, 'data' => []]);
}
//类未找到, 返回错误页面
if (($e instanceof ClassNotFoundException || $e instanceof RouteNotFoundException) || ($e instanceof HttpException && $e->getStatusCode() == 404)) {
return view(root_path() . 'public/error/404.html', ['e' => $e], 404);
}
// ajax请求500异常, 不返回错误页面
if (($e instanceof Exception || $e instanceof PDOException || $e instanceof HttpException || $e instanceof InvalidArgumentException || $e instanceof ErrorException || $e instanceof ParseError || $e instanceof TypeError) && request()->isAjax()) {
return json(['msg' => env('app_debug') ? $e->getMessage() : '系统异常,请稍后再试', 'code' => 500, 'data' => env('app_debug') ? $e->getTrace() : []]);
}
// 内部异常 , 返回错误页面
if ($e instanceof Exception || $e instanceof PDOException || $e instanceof InvalidArgumentException || $e instanceof ErrorException || $e instanceof ParseError || $e instanceof TypeError || ($e instanceof HttpException && $e->getStatusCode() == 500)) {
return view(root_path() . 'public/error/500-2.html', ['e' => $e, 'error_num' => $this->error_num], 500);
}
}
// 其他错误交给系统处理
return parent::render($request, $e);
}
/**
* 记录异常信息(包括日志或者其它方式记录)
*
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $e): void
{
if (!$this->isIgnoreReport($e)) {
if (
$e instanceof ClassNotFoundException || ($e instanceof HttpException && $e->getStatusCode() == 404)
|| stripos($e->getMessage(), "Undefined index:") !== false
|| stripos($e->getMessage(), "Undefined offset:") !== false
|| stripos($e->getMessage(), "Undefined variable:") !== false
) {
//这是由Mongo日志记录抛出的异常 , 不能再把异常推进队列中, 否则会造成死循环
//这里用原生report方法 , 防止死循环
//类不存在的也不记录mongo日志
//变量未定义 , 也不记录mongo日志
parent::report($e);
return;
}
try {
//取出异常的部分堆栈参数,方便排查问题
$args = $e->getTrace();
$error_args = array_slice($args, 0, 5);
foreach ($error_args as $k => $error) {
if (!isset($error['file'])) {
unset($error_args[$k]);
} else if (isset($error['file']) && (stripos($error['file'], 'thinkphp') || stripos($error['file'], 'index.php'))) {
unset($error_args[$k]);
}
}
//异常信息丢到队列处理
QueueHelper::pushQueue("app\job\LogErrorMsg", "log_error_msg", [
'msg_type' => 'exception',
'system' => env('system_name'),
'error_num' => $this->error_num,
'error_handle' => [
'file' => $e->getFile(),
'line' => $e->getLine(),
'msg' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'trace_args' => $error_args,
],
]);
return;
} catch (\ErrorException $error) {
//入队异常 , 用原生report方法 , 防止死循环
parent::report($e);
return;
} catch (\Exception $error) {
//入队异常 , 用原生report方法 , 防止死循环
parent::report($e);
return;
}
// 使用内置的方式记录异常日志
parent::report($e);
return;
}
}
}
最佳答案
