>  기사  >  백엔드 개발  >  PHP5에서의 오류 처리 및 문제 위치 소개(코드 예)

PHP5에서의 오류 처리 및 문제 위치 소개(코드 예)

不言
不言앞으로
2019-01-09 10:38:102533검색

이 글은 PHP5에서의 오류 처리 및 문제 위치에 대한 소개를 제공합니다(코드 예제). 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

PHP에서 E_ERROR 수준의 치명적인 런타임 오류가 발생했을 때 문제를 찾는 방법에 대해 이야기해 보겠습니다. 예를 들어 치명적인 오류: 허용된 크기의 메모리 메모리 오버플로입니다. 이런 종류의 오류가 발생하면 프로그램이 바로 종료됩니다. 오류가 보고된 특정 파일과 코드 줄 수를 나타내는 오류 로그가 PHP 오류 로그에 기록되며 다른 정보는 손실되지 않습니다. PHP7이라면 예외와 같은 오류를 잡을 수 있지만 PHP5에서는 그렇지 않습니다. Fatal error: Allowed memory size of内存溢出这种。当出现这种错误时会导致程序直接退出,PHP的error log中会记录一条错误日志说明报错的具体文件和代码行数,其它的任何信息都没有了。如果是PHP7的话还可以像捕获异常一样捕获错误,PHP5的话就不行了。

一般想到的方法就是看看报错的具体代码,如果报错文件是CommonReturn.class.php像下面这个样子。

<?php

/**
 * 公共返回封装
 * Class CommonReturn
 */
class CommonReturn
{

    /**
     * 打包函数
     * @param     $params
     * @param int $status
     *
     * @return mixed
     */
    static public function packData($params, $status = 0)
    {
        $res[&#39;status&#39;] = $status;
        $res[&#39;data&#39;] = json_encode($params);
        return $res;
    }

}

其中json_encode那一行报错了,然后你查了下packData这个方法,有很多项目的类中都有调用,这时要怎么定位问题呢?

场景复现

好,首先我们复现下场景。假如实际调用的程序bug.php如下

<?php

require_once &#39;./CommonReturn.class.php&#39;;

$res = ini_set(&#39;memory_limit&#39;, &#39;1m&#39;);

$res = [];
$char = str_repeat(&#39;x&#39;, 999);
for ($i = 0; $i < 900 ; $i++) {
    $res[] = $char;
}

$get_pack = CommonReturn::packData($res);

// something else

运行bug.php PHP错误日志中会记录

[08-Jan-2019 11:22:52 Asia/Shanghai] PHP Fatal error:  Allowed memory size of 1048576 bytes exhausted (tried to allocate 525177 bytes) in /CommonReturn.class.php on line 20

复现成功,错误日志中只是说明了报错的文件和哪行代码,无法知道程序的上下文堆栈信息,不知道具体是哪块业务逻辑调用的,这样一来就无法定位修复错误。如果是偶尔出现,并且没有来自前端业务的反馈要怎么排查呢。

解决思路

1、有人想到了修改memory_limit增加内存分配,但这种方法治标不治本。做开发肯定要找到问题的根源。

2、开启core dump,如果生成code文件可以进行调试,但是发现code只有进程异常退出才会生成。像E_ERROR级别的错误不一定会生成code文件,内存溢出这种可能PHP内部自己就处理了。

3、使用register_shutdown_function注册一个PHP终止时的回调函数,再调用error_get_last如果获取到了最后发生的错误,就通过debug_print_backtrace获取程序的堆栈信息,我们试试看。

修改CommonReturn.class.php文件如下

<?php

/**
 * 公共返回封装
 * Class CommonReturn
 */
class CommonReturn
{

    /**
     * 打包函数
     * @param     $params
     * @param int $status
     *
     * @return mixed
     */
    static public function packData($params, $status = 0)
    {

        register_shutdown_function([&#39;CommonReturn&#39;, &#39;handleFatal&#39;]);

        $res[&#39;status&#39;] = $status;
        $res[&#39;data&#39;] = json_encode($params);
        return $res;
    }

    /**
     * 错误处理
     */
    static protected function handleFatal()
    {
        $err = error_get_last();
        if ($err[&#39;type&#39;]) {
            ob_start();
            debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
            $trace = ob_get_clean();
            $log_cont = &#39;time=%s&#39; . PHP_EOL . &#39;error_get_last:%s&#39; . PHP_EOL . &#39;trace:%s&#39; . PHP_EOL;
            @file_put_contents(&#39;/tmp/debug_&#39; . __FUNCTION__ . &#39;.log&#39;, sprintf($log_cont, date(&#39;Y-m-d H:i:s&#39;), var_export($err, 1), $trace), FILE_APPEND);
        }

    }

}

再次运行bug.php,日志如下。

error_get_last:array (
  &#39;type&#39; => 1,
  'message' => 'Allowed memory size of 1048576 bytes exhausted (tried to allocate 525177 bytes)',
  'file' => '/CommonReturn.class.php',
  'line' => 23,
)
trace:#0  CommonReturn::handleFatal()

回溯信息没有来源,尴尬了。猜测因为backtrace信息保存在内存中,当出现致命错误时会清空。没办法,把backtrace从外面传进来试试。再次修改CommonReturn.class.php。

<?php

/**
 * 公共返回封装
 * Class CommonReturn
 */
class CommonReturn
{

    /**
     * 打包函数
     * @param     $params
     * @param int $status
     *
     * @return mixed
     */
    static public function packData($params, $status = 0)
    {

        ob_start();
        debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
        $trace = ob_get_clean();
        register_shutdown_function([&#39;CommonReturn&#39;, &#39;handleFatal&#39;], $trace);

        $res[&#39;status&#39;] = $status;
        $res[&#39;data&#39;] = json_encode($params);
        return $res;
    }

    /**
     * 错误处理
     * @param $trace
     */
    static protected function handleFatal($trace)
    {
        $err = error_get_last();
        if ($err[&#39;type&#39;]) {
            $log_cont = &#39;time=%s&#39; . PHP_EOL . &#39;error_get_last:%s&#39; . PHP_EOL . &#39;trace:%s&#39; . PHP_EOL;
            @file_put_contents(&#39;/tmp/debug_&#39; . __FUNCTION__ . &#39;.log&#39;, sprintf($log_cont, date(&#39;Y-m-d H:i:s&#39;), var_export($err, 1), $trace), FILE_APPEND);
        }

    }

}

再次运行bug.php

일반적으로 생각되는 방법은 에러 리포트의 구체적인 코드를 살펴보는 것입니다. 에러 리포트 파일이 CommonReturn.class.php라면 아래와 같습니다.

error_get_last:array (
  &#39;type&#39; => 1,
  'message' => 'Allowed memory size of 1048576 bytes exhausted (tried to allocate 525177 bytes)',
  'file' => '/CommonReturn.class.php',
  'line' => 26,
)
trace:#0  CommonReturn::packData() called at [/bug.php:13]
json_encode 라인에서 오류가 보고된 후 packData 메서드를 확인했습니다. 현재 문제를 어떻게 찾을 수 있나요?

장면 재현

  1. 자, 먼저 장면을 재현해 보겠습니다. 실제로 호출된 프로그램인 bug.php가 다음과 같다면

    rrreee

    bug.php를 실행하면 PHP 에러 로그에
  2. rrreee
  3. 이 기록됩니다. 재생에 성공하면 에러 로그가 나옵니다. 오류를 보고한 파일만 명시되어 있으며, 어떤 코드 줄에 대해서는 프로그램의 컨텍스트 스택 정보와 어떤 특정 비즈니스 로직이 호출되었는지 알 수 없으므로 오류를 찾아 수정하는 것이 불가능합니다. 간헐적으로 발생하는데 프론트엔드 업무에서 피드백이 없는 경우 문제 해결 방법.


    Solution

1. 어떤 사람들은 메모리 할당을 늘리기 위해 memory_limit를 수정하려고 생각했지만 이 방법은 증상을 치료할 뿐 근본 원인을 치료하지는 않습니다. 개발을 할 때에는 문제의 근본 원인을 찾아야 합니다.

    2.코드 파일이 생성되면 디버깅이 가능하지만, 프로세스가 비정상적으로 종료된 경우에만 코드가 생성되는 것으로 확인되었습니다. E_ERROR 수준의 오류는 반드시 코드 파일을 생성하지 않을 수도 있습니다. 메모리 오버플로 가능성은 PHP에서 내부적으로 처리됩니다.
  1. 3. PHP 종료 시 콜백 함수를 등록하기 위해 Register_shutdown_function을 사용하고, 마지막으로 발생한 오류가 발생하면 debug_print_backtrace를 사용하여 프로그램의 스택 정보를 얻으십시오.

  2. CommonReturn.class.php 파일을 다음과 같이 수정합니다.
rrreee
bug.php를 다시 실행하면 로그는 다음과 같습니다.

rrreee

트레이스백 정보에는 출처가 없어 당황스럽습니다. 역추적 정보는 메모리에 저장했다가 치명적인 오류가 발생하면 지워지기 때문인 것 같아요. 다른 방법은 없습니다. 외부에서 역추적을 전달해 보세요. CommonReturn.class.php를 다시 수정하세요. #🎜🎜#rrreee#🎜🎜#bug.php를 다시 실행하면 로그는 다음과 같습니다. #🎜🎜#rrreee#🎜🎜#bug.php의 13번째 줄에서 호출 소스를 성공적으로 찾았습니다. 최종 CommonReturn.class.php를 프로덕션 환경에 게시하고, 다시 오류가 발생하면 로그를 살펴보세요. 그러나 이 경우 packData를 호출하는 모든 프로그램은 추적 기능을 실행하므로 성능에 확실히 영향을 미칩니다. #🎜🎜##🎜🎜##🎜🎜#Summary#🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜#사용된 Register_shutdown_function 함수에 주의해야 합니다. 여러 개의 다른 콜백을 등록할 수 있습니다. 하지만 특정 콜백 함수가 종료되면 나중에 등록된 콜백 함수는 모두 실행되지 않습니다. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#debug_print_backtrace 역추적 정보를 얻는 첫 번째 함수에는 요청 매개변수가 포함됩니다. 두 번째 함수는 역추적 레코드 레이어 수를 저장하기 위한 요청 매개변수를 반환하지 않습니다. 약간의 돈이 필요하며 요청 매개변수가 큰 경우 이 함수를 호출하면 메모리가 직접 오버플로될 수 있습니다. #🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜#가장 좋은 방법은 PHP7을 업그레이드하는 것인데, 예외와 같은 오류를 잡을 수 있습니다. . #🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜#

위 내용은 PHP5에서의 오류 처리 및 문제 위치 소개(코드 예)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제