<?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;
}
}
}
ThinkPHP 是一个免费开源的,快速、简单的面向对象的 轻量级PHP开发框架 ,创立于2006年初,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业应用开发而诞生的。ThinkPHP从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,也注重易用性。并且拥有众多的原创功能和特性,在社区团队的积极参与下,在易用性、扩展性和性能方面不断优化和改进,已经成长为国内最领先和最具影响力的WEB应用开发框架,众多的典型案例确保可以稳定用于商业以及门户级的开发。