原文:拦截PHP各种异常和错误,发生致命错误时进行报警,万事防患于未然

在日常开发中,大多数人的做法是在开发环境时开启调试模式,在产品环境关闭调试模式。在开发的时候可以查看各种错误、异常,但是在线上就把错误显示的关闭。

上面的情形看似很科学,有人解释为这样很安全,别人看不到错误,以免泄露重要信息...

但是你有没有遇到这种情况,线下好好的,一上线却运行不起来也找不到原因...

一个脚本,跑了好长一段时间,一直没有问题,有一天突然中断了,然后了也没有任何记录都不造啥原因...

线上一个付款,别人明明付了款,但是我们却没有记录到,自己亲自去实验,却是好的...

种种以上,都是因为大家关闭了错误信息,并且未将错误、异常记录到日志,导致那些随机发生的错误很难追踪。这样矛盾就来了,即不要显示错误,又要追踪错误,这如何实现了?

以上问题都可以通过PHP的错误、异常机制及其内建函数'set_exception_handler','set_error_handler','register_shutdown_function' 来实现

'set_exception_handler' 函数 用于拦截各种未捕获的异常,然后将这些交给用户自定义的方式进行处理

'set_error_handler' 函数可以拦截各种错误,然后交给用户自定义的方式进行处理

'register_shutdown_function' 函数是在PHP脚本结束时调用的函数,配合'error_get_last'可以获取最后的致命性错误

这个思路大体就是把错误、异常、致命性错误拦截下来,交给我们自定义的方法进行处理,我们辨别这些错误、异常是否致命,如果是则记录的数据库或者文件系统,然后使用脚本不停的扫描这些日志,发现严重错误立即发送邮件或发送短信进行报警

首先我们定义错误拦截类,该类用于将错误、异常拦截下来,用我们自己定义的处理方式进行处理,该类放在文件名为'errorHandler.class.php'中,代码如下

/*** 文件名称:baseErrorHandler.class.php* 摘    要:错误拦截器父类*/
require 'errorHandlerException.class.php';//异常类
class errorHandler
{public $argvs = array();public     $memoryReserveSize = 262144;//备用内存大小private $_memoryReserve;//备用内存/*** 方      法:注册自定义错误、异常拦截器* 参      数:void* 返      回:void*/public function register(){ini_set('display_errors', 0);set_exception_handler(array($this, 'handleException'));//截获未捕获的异常set_error_handler(array($this, 'handleError'));//截获各种错误 此处切不可掉换位置//留下备用内存 供后面拦截致命错误使用$this->memoryReserveSize > 0 && $this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);register_shutdown_function(array($this, 'handleFatalError'));//截获致命性错误
    }/*** 方      法:取消自定义错误、异常拦截器* 参      数:void* 返      回:void*/public function unregister(){restore_error_handler();restore_exception_handler();}/*** 方      法:处理截获的未捕获的异常* 参      数:Exception $exception* 返      回:void*/public function handleException($exception){$this->unregister();try{$this->logException($exception);exit(1);}catch(Exception $e){exit(1);}}/*** 方      法:处理截获的错误* 参      数:int     $code 错误代码* 参      数:string $message 错误信息* 参      数:string $file 错误文件* 参      数:int     $line 错误的行数* 返      回:boolean*/public function handleError($code, $message, $file, $line){//该处思想是将错误变成异常抛出 统一交给异常处理函数进行处理if((error_reporting() & $code) && !in_array($code, array(E_NOTICE, E_WARNING, E_USER_NOTICE, E_USER_WARNING, E_DEPRECATED))){//此处只记录严重的错误 对于各种WARNING NOTICE不作处理$exception = new errorHandlerException($message, $code, $code, $file, $line);$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);array_shift($trace);//trace的第一个元素为当前对象 移除foreach($trace as $frame) {if($frame['function'] == '__toString') {//如果错误出现在 __toString 方法中 不抛出任何异常$this->handleException($exception);exit(1);}}throw $exception;}return false;}/*** 方      法:截获致命性错误* 参      数:void* 返      回:void*/public function handleFatalError(){unset($this->_memoryReserve);//释放内存供下面处理程序使用$error = error_get_last();//最后一条错误信息if(errorHandlerException::isFatalError($error)){//如果是致命错误进行处理$exception = new errorHandlerException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);$this->logException($exception);exit(1);}}/*** 方      法:获取服务器IP* 参      数:void* 返      回:string*/final public function getServerIp(){$serverIp = '';if(isset($_SERVER['SERVER_ADDR'])){$serverIp = $_SERVER['SERVER_ADDR'];}elseif(isset($_SERVER['LOCAL_ADDR'])){$serverIp = $_SERVER['LOCAL_ADDR'];}elseif(isset($_SERVER['HOSTNAME'])){$serverIp = gethostbyname($_SERVER['HOSTNAME']);}else{$serverIp = getenv('SERVER_ADDR');}        return $serverIp; }/*** 方      法:获取当前URI信息* 参      数:void* 返      回:string $url*/public function getCurrentUri(){$uri = '';if($_SERVER ["REMOTE_ADDR"]){//浏览器浏览模式$uri = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];}else{//命令行模式$params = $this->argvs;$uri = $params[0];array_shift($params);for($i = 0, $len = count($params); $i < $len; $i++){$uri .= ' ' . $params[$i];}}return $uri;}/*** 方      法:记录异常信息* 参      数:errorHandlerException $e 错误异常* 返      回:boolean 是否保存成功*/final public function logException($e){$error = array('add_time'     =>     time(),'title'     =>     errorHandlerException::getName($e->getCode()),//这里获取用户友好型名称'message'     =>     array(),'server_ip' =>     $this->getServerIp(),'code'         =>     errorHandlerException::getLocalCode($e->getCode()),//这里为各种错误定义一个编号以便查找'file'         =>  $e->getFile(),'line'         =>     $e->getLine(),'url'        =>  $this->getCurrentUri(),);do{//$e->getFile() . ':' . $e->getLine() . ' ' . $e->getMessage() . '(' . $e->getCode() . ')'$message = (string)$e;$error['message'][] = $message;} while($e = $e->getPrevious());$error['message'] = implode("\r\n", $error['message']);$this->logError($error);}/*** 方      法:记录异常信息* 参      数:array $error = array(*                                    'time' => int, *                                    'title' => 'string', *                                    'message' => 'string', *                                    'code' => int,*                                    'server_ip' => 'string'*                                     'file'     =>  'string',*                                    'line' => int,*                                    'url' => 'string',*                                );* 返      回:boolean 是否保存成功*/public function logError($error){/*这里去实现如何将错误信息记录到日志*/}
}

上述代码中,有个'errorHandlerException'类,该类放在文件'errorHandlerException.class.php'中,该类用于将错误转换为异常,以便记录错误发生的文件、行号、错误代码、错误信息等信息,同时其方法'isFatalError'用于辨别该错误是否是致命性错误。这里我们为了方便管理,将错误进行编号并命名。该类的代码如下

/*** 文件名称:errorHandlerException.class.php* 摘    要:自定义错误异常类 该类继承至PHP内置的错误异常类*/
class errorHandlerException extends ErrorException
{public static $localCode = array(E_COMPILE_ERROR => 4001,E_COMPILE_WARNING => 4002,E_CORE_ERROR => 4003,E_CORE_WARNING => 4004,E_DEPRECATED => 4005,E_ERROR => 4006,E_NOTICE => 4007,E_PARSE => 4008,E_RECOVERABLE_ERROR => 4009,E_STRICT => 4010,E_USER_DEPRECATED => 4011,E_USER_ERROR => 4012,E_USER_NOTICE => 4013,E_USER_WARNING => 4014,E_WARNING => 4015,4016 => 4016,);public static $localName = array(E_COMPILE_ERROR => 'PHP Compile Error',E_COMPILE_WARNING => 'PHP Compile Warning',E_CORE_ERROR => 'PHP Core Error',E_CORE_WARNING => 'PHP Core Warning',E_DEPRECATED => 'PHP Deprecated Warning',E_ERROR => 'PHP Fatal Error',E_NOTICE => 'PHP Notice',E_PARSE => 'PHP Parse Error',E_RECOVERABLE_ERROR => 'PHP Recoverable Error',E_STRICT => 'PHP Strict Warning',E_USER_DEPRECATED => 'PHP User Deprecated Warning',E_USER_ERROR => 'PHP User Error',E_USER_NOTICE => 'PHP User Notice',E_USER_WARNING => 'PHP User Warning',E_WARNING => 'PHP Warning',4016 => 'Customer`s Error',);/*** 方      法:构造函数* 摘      要:相关知识请查看 http://php.net/manual/en/errorexception.construct.php*   * 参      数:string        $message     异常信息(可选)*              int         $code         异常代码(可选)*              int         $severity*              string     $filename     异常文件(可选)*              int         $line         异常的行数(可选)*           Exception  $previous   上一个异常(可选)** 返      回:void*/public function __construct($message = '', $code = 0, $severity = 1, $filename = __FILE__, $line = __LINE__, Exception $previous = null){parent::__construct($message, $code, $severity, $filename, $line, $previous);}/*** 方      法:是否是致命性错误* 参      数:array $error* 返      回:boolean*/public static function isFatalError($error){$fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR,E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING);return isset($error['type']) && in_array($error['type'], $fatalErrors);}/*** 方      法:根据原始的错误代码得到本地的错误代码* 参      数:int $code* 返      回:int $localCode*/public static function getLocalCode($code){return isset(self::$localCode[$code]) ? self::$localCode[$code] : self::$localCode[4016];}/*** 方      法:根据原始的错误代码获取用户友好型名称* 参      数:int * 返      回:string $name*/public static function getName($code){return isset(self::$localName[$code]) ? self::$localName[$code] : self::$localName[4016];}

在错误拦截类中,需要用户自己定义实现错误记录的方法('logException'),这个地方需要注意,有些错误可能在一段时间内不断发生,因此只需记录一次即可,你可以使用错误代码、文件、行号、错误详情 生成一个MD5值用于记录该错误是否已经被记录,如果在规定时间内(一个小时)已经被记录过则不需要再进行记录

然后我们定义一个文件,用于实例化以上类,捕获各种错误、异常,该文件命名为'registerErrorHandler.php', 内如如下

/*
* 使用方法介绍:
* 在入口处引入该文件即可,然后可以在该文件中定义调试模式常量'DEBUG_ERROR'
*
* <?php
*
*    require 'registerErrorHandler.php';
*
* ?>
*//**
* 调试错误模式:
* 0                =>            非调试模式,不显示异常、错误信息但记录异常、错误信息
* 1                =>            调试模式,显示异常、错误信息但不记录异常、错误信息
*/
define('DEBUG_ERROR', 0);
require 'errorHandler.class.php';class registerErrorHandler
{/*** 方      法:注册异常、错误拦截* 参      数:void* 返      回:void*/public static function register(){global $argv;if(DEBUG_ERROR){//如果开启调试模式ini_set('display_errors', 1);return;}//如果不开启调试模式ini_set('error_reporting', -1);ini_set('display_errors', 0);$handler = new errorHandler();$handler->argvs = $argv;//此处主要兼容命令行模式下获取参数$handler->register();}
}
registerErrorHandler::register();

剩下的就是需要你在你的入口文件引入该文件,定义调试模式,然后实现你自己记录错误的方法即可

需要注意的是,有些错误在你进行注册之前已经发生并且导致脚本中断是无法记录下来的,因为此时'registerErrorHandler::register()' 尚未执行已经中断了

还有就是'set_error_handler'这个函数不能捕获下面类型的错误 E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNINGE_COMPILE_ERROR、 E_COMPILE_WARNING, 这个可以在官方文档中看到,但是本处无妨,因为以上错误是解析、编译错误,这些都没有通过,你是不可能发布上线的

以上代码经过严格测试,并且已经应用在线上环境,大家可以根据自己需要进行更改使用

拦截PHP各种异常和错误,发生致命错误时进行报警,万事防患于未然相关推荐

  1. tenacity发生异常/失败/错误时重试retry机制,Python

    tenacity发生异常/失败/错误时重试retry机制,Python 安装: pip install tenacity 示例: @retry def non_stop():print("永 ...

  2. 内存错误 未处理的异常: 0xC0000005: 读取位置 0x00000000 时发生访问冲突

    其他:内存错误 未处理的异常: 0xC0000005: 读取位置 0x00000000 时发生访问冲突 关于0xC0000005问题: 0xC0000005: Access Violation错误调试 ...

  3. php try报错程序中断,php7异常与错误处理和自定义异常

    7.2版本异常与错误的概述 什么叫做异常? 异常是指程序运行中不符合预期情况以及与正常流程不同的状况. 比如你链接数据库,在参数都写上去的条件下,发现链接不上去,这就属于不符合预期. 可以被 try- ...

  4. PHP ERROR_php中的异常和错误浅析

    本文主要介绍了php中的异常和错误,分享给大家供大家参考学习,下面来一起看看详细的介绍: 一.异常与错误 异常是指程序运行中不符合预期情况以及与正常流程不同的状况.错误则属于自身问题,是一种非法语法或 ...

  5. php 错误 异常,php中的异常和错误解析

    PHP错误是属于php程序自身的问题,一般是由非法的语法,环境问题导致的,使得编译器无法通过检查甚至无法运行的情况.PHP异常一般是业务逻辑上出现的不合预期.与正常流程不同的状况,不是语法错误.本文介 ...

  6. assertionerror是什么异常_php7异常与错误处理和自定义异常

    7.2版本异常与错误的概述 什么叫做异常? 异常是指程序运行中不符合预期情况以及与正常流程不同的状况. 比如你链接数据库,在参数都写上去的条件下,发现链接不上去,这就属于不符合预期. 可以被 try- ...

  7. java 返回ro,错误retrofit rxjava优雅的处理服务器返回异常、错误

    Retrofit+RxJava优雅的处理服务器 返 回异常.错误 异常&错误 实际开发经常有这种情况比如登录请求接口返回的信息包括请求返回的状态失败还是成功错误码 Us er对象等等 ...

  8. php中各种报错处理,PHP中的异常和错误处理

    在PHP5中所有错误异常都是基于Exception类,所有具体的异常类都是Exception的子类.而所有的errors都是致命错误或是可恢复性致命错误,是不能被捕获的(只会强制终止脚本) 在PHP7 ...

  9. php throwable,PHP7异常与错误处理机制 Exception, Error, Throwable

    PHP 中的 Exception, Error, ThrowablePHP 中将代码自身异常(一般是环境或者语法**所致)称作错误 Error,将运行**现的逻辑错误称为异常 Exception 错误 ...

最新文章

  1. C++异常实现与longjmp, setjmp,栈指针EBP, Active Record
  2. Java 7:WatchService
  3. Hbase JMX 监控 - Region
  4. Nginx安装出现错误解决记录
  5. 接口传递的json后台如何获得值
  6. GWR4软件怎么用+结果解读+结果在ArcGIS中可视化
  7. 【金蝶】金蝶KIS专业版9.1清理用户名密码
  8. win10分辨率不能调整_WIN10屏幕分辨率不能调整的解决技巧
  9. gson的解析demo JAVA
  10. c++ qt 操作Excel 实现冻结窗格的功能。
  11. 跌倒智能监测警报系统市场现状及未来发展趋势分析
  12. 只因为给我打了0.1分,重新撸了个记账小程序
  13. 适用于WF4.0的流程设计器 (Wxd.WF,BPM.Foundation,Wxwinter.WF 升级用)
  14. C++版俄罗斯方块(不是C语言哦)
  15. flashfxp支持sftp了
  16. scrapy的crawlspider爬虫
  17. 2022卡塔尔世界杯观赛球迷总消费已赶超前两届,美国、墨西哥和沙特消费最多 | 美通社头条...
  18. Ad Hoc Networks TOPIC THREE <IP Addressing and Internet Protocol>【Personal Notes】
  19. Clickhouse 使用DBeaver连接
  20. 哈尔滨理工大学软件与微电子学院程序设计竞赛(19级新生赛)——题解

热门文章

  1. MySQL Workbench 怎么创建数据库
  2. Android Fragment中ListView的OnItemClickListener无效
  3. 搭建yum源,配置yum源
  4. 第三部分:MFC中控件的样式
  5. 15套漂亮的 PSD 格式的图标,不一样的视觉效果
  6. PHPEXCEL使用实例
  7. “外星人”字符串生成算法研究
  8. 如果检测指定的Windows服务是否启动
  9. Redhat安装gtk2.0和pkg-config
  10. DOS命令-创建删除文件和文件夹