基于thinkphp实现异常日志详细统计功能

2020-05-01  本文已影响0人  鸿雁长飞光不度

后端的代码基于thinkphp框架开发,随着业务的增加,代码复杂度不断增多,而且有好几份代码,可能部署在不同的服务器上。即使在测试服务器上经过严格测试,正式环境有时也很难避免出现bug,所以需要较为详细的日志来记录,而且日志要有统一的位置存放。

1.thinkphp本身的异常处理

稍微熟悉thinkphp的都应该知道,它的异常可以自己定义,只要自定定一个handle类,继承think\exception\handle,然后重写里面的render方法,然后在配置文件配置自己定义的异常处理类,就可以根据业务场景的不同,以不同的形式输出异常信息,比如json、html。所以一个工程中是存在多个think\exception\handle的子类的。对应异常信息的记录,其实tp本身是有记录的。

think\Error类异常处理的代码如下。

 /**
     * Exception Handler
     * @access public
     * @param  \Exception|\Throwable $e
     */
    public static function appException($e)
    {
        if (!$e instanceof \Exception) {
            $e = new ThrowableError($e);
        }

        self::getExceptionHandler()->report($e);

        if (PHP_SAPI == 'cli') {
            self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
        } else {
            self::getExceptionHandler()->render($e)->send();
        }
    }

    public static function appError($errno, $errstr, $errfile = '', $errline = 0)
    {
        $exception = new ErrorException($errno, $errstr, $errfile, $errline);
        if (error_reporting() & $errno) {
            // 将错误信息托管至 think\exception\ErrorException
            throw $exception;
        }

        self::getExceptionHandler()->report($exception);
    }

其中self::getException()就是根据配置获取处理异常的handle实例,然后调用了该实例的report方法。所以report方法是所有异常必经之路。经过report处理以后,才会根据php当前运行模式进行渲染输出。

2.thinkphp异常记录的不足

点击report跳转到think\exception\handle

 public function report(Exception $exception)
    {
        if (!$this->isIgnoreReport($exception)) {
            // 收集异常数据
            if (Container::get('app')->isDebug()) {
                $data = [
                    'file'    => $exception->getFile(),
                    'line'    => $exception->getLine(),
                    'message' => $this->getMessage($exception),
                    'code'    => $this->getCode($exception),
                ];
                $log = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]";
            } else {
                $data = [
                    'code'    => $this->getCode($exception),
                    'message' => $this->getMessage($exception),
                ];
                $log = "[{$data['code']}]{$data['message']}";
            }

            if (Container::get('app')->config('log.record_trace')) {
                $log .= "\r\n" . $exception->getTraceAsString();
            }

            Container::get('log')->record($log, 'error');
        }
    }

可以看到这是thinkphp默认的异常记录,缺点如下:

3.我们如何改进

所以我们要强化日志记录功能,因为report方法是tp记录日志的方法,所以显然我们定义一个baseHandle继承 think\exception\handle,然后在baseHandle重写report方法就可以了,然后工程中所有的handle都继承baseHandle,异常自然就会被自定义的report方法里面逻辑处理。到底记录这些异常信息,有很多方案。这里我们用的是mongo存储,尽可能详情的记录。无论如何,把数据都记录在一块了,然后后台增加个查看异常信息的功能,幸福还会远吗?

class BaseHandle extends parentHandle
{

    public function report(Exception $exception)
    {
        parent::report($exception); // TODO: Change the autogenerated stub

        try{
            //        //记录日志
            $exceptionLogParam = new ExceptionLogParam();
            $exceptionLogParam->exception = $exception->getMessage();
            $exceptionLogParam->trace = $exception->getTraceAsString();
            $exceptionLogParam->is_cli = PHP_SAPI == 'cli' ? 1 : 0;
            $exceptionLogParam->header = \GuzzleHttp\json_encode(Request::header(), JSON_UNESCAPED_UNICODE);
            $exceptionLogParam->param = \GuzzleHttp\json_encode(Request::param(), JSON_UNESCAPED_UNICODE);
            $exceptionLogParam->method = \GuzzleHttp\json_encode(Request::method(), JSON_UNESCAPED_UNICODE);
            $exceptionLogParam->controller = Loader::parseName(Request::controller(), 0);
            $exceptionLogParam->param = Request::param();
            $exceptionLogParam->param = \GuzzleHttp\json_encode($exceptionLogParam->param, JSON_UNESCAPED_UNICODE);
            $exceptionLogParam->action = Request::action();
            $exceptionLogParam->module = Request::module();
            $exceptionLogParam->user_id = RememberMeHelper::$_userId;
            $exceptionLogParam->created_at = time();
            $exceptionLogParam->cookie = \GuzzleHttp\json_encode(Request::cookie(),true);
            $exceptionLogParam->session = \GuzzleHttp\json_encode(Request::session(),true);
            $exceptionLogParam->url = Request::url();
            $exceptionLogParam->server_ip = get_server_ip();
            $exceptionLogParam->file_line_info = $exception->getFile().$exception->getLine();
            ExceptionLogService::getInstance()->create($exceptionLogParam);
        }catch (\Exception $exception){
            \think\facade\Log::write('存储日志发生了异常');
        }
    }
}

考虑过的其他存储方法:

上一篇 下一篇

猜你喜欢

热点阅读