PHP/Laravel中的“异常链”

之前知道,Python 3 中终于实现了对异常链的支持。

最近,在代码中,有一处写着:

try {
    // some code
} catch (\Exception $e) {
    \Log::error('xxx_id' . $xxx->id);
}

问了之后才发现,这是需要知道是哪个id触发的错误,如果不捕获异常的话,在backtrace信息中可能不会出现此id。

但是,这样做虽然可以达到他的目的,但是掩盖了底层的错误,对后续的追查不利。

然后,作为(伪)Python开发者的我就想起了Python3中的异常链技术,所以找了下PHP中有没有类似的东西,结果发现:自PHP 5.3开始,支持了类似的技术

解决方案:可以自己扩展Excepiton类,默认的构造方法里面,第三个参数是前一个异常,经过测试,在Laravel框架中,同一条链中的Exception都会展示出来(无论是在Console输出,还是日志输出中)。


好,让我们来自己试一试。

首先写一个Command:

class NestExceptionTest extends Command
{
    protected $name = 'NestExceptionTest';

    public function fire()
    {
        try {
            $this->testOneLevel();
        } catch (\Exception $e) {
            throw new \Exception("top level", 0, $e);
        }
    }

    private function testOneLevel()
    {
        try {
            $this->testTwoLeve();
        } catch (\Exception $e) {
            throw new \Exception('one level', 0, $e);
        }
    }

    private function testTwoLeve()
    {
        throw new \Exception('two level');
    }
}

为了偷懒,就没有自己定义自己的Exception,仅仅是不同的错误信息来区分。

执行此命令,输出如下:

Exception output console同时,日志输出如下:

[2015-12-12 13:54:06] production.ERROR: exception 'Exception' with message 'two level' in /Users/robberphex/projects/xxx/app/Console/Commands/NestExceptionTest.job:31
Stack trace:
#0 /Users/robberphex/projects/xxx/app/Console/Commands/NestExceptionTest.job(23): App\Console\Commands\SiteMap\SiteMapBuild->testTwoLeve()
#1 /Users/robberphex/projects/xxx/app/Console/Commands/NestExceptionTest.job(14): App\Console\Commands\SiteMap\SiteMapBuild->testOneLevel()
#2 [internal function]: App\Console\Commands\SiteMap\SiteMapBuild->fire()
#3 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Container/Container.php(503): call_user_func_array(Array, Array)
#4 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Console/Command.php(150): Illuminate\Container\Container->call(Array)
#5 /Users/robberphex/projects/xxx/vendor/symfony/console/Command/Command.php(256): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#6 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Console/Command.php(136): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#7 /Users/robberphex/projects/xxx/vendor/symfony/console/Application.php(838): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 /Users/robberphex/projects/xxx/vendor/symfony/console/Application.php(189): Symfony\Component\Console\Application->doRunCommand(Object(App\Console\Commands\SiteMap\SiteMapBuild), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 /Users/robberphex/projects/xxx/vendor/symfony/console/Application.php(120): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(107): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 /Users/robberphex/projects/xxx/artisan(36): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#12 {main}

Next exception 'Exception' with message 'one level' in /Users/robberphex/projects/xxx/app/Console/Commands/NestExceptionTest.job:25
Stack trace:
#0 /Users/robberphex/projects/xxx/app/Console/Commands/NestExceptionTest.job(14): App\Console\Commands\SiteMap\SiteMapBuild->testOneLevel()
#1 [internal function]: App\Console\Commands\SiteMap\SiteMapBuild->fire()
#2 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Container/Container.php(503): call_user_func_array(Array, Array)
#3 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Console/Command.php(150): Illuminate\Container\Container->call(Array)
#4 /Users/robberphex/projects/xxx/vendor/symfony/console/Command/Command.php(256): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#5 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Console/Command.php(136): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#6 /Users/robberphex/projects/xxx/vendor/symfony/console/Application.php(838): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#7 /Users/robberphex/projects/xxx/vendor/symfony/console/Application.php(189): Symfony\Component\Console\Application->doRunCommand(Object(App\Console\Commands\SiteMap\SiteMapBuild), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 /Users/robberphex/projects/xxx/vendor/symfony/console/Application.php(120): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(107): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 /Users/robberphex/projects/xxx/artisan(36): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 {main}

Next exception 'Exception' with message 'top level' in /Users/robberphex/projects/xxx/app/Console/Commands/NestExceptionTest.job:16
Stack trace:
#0 [internal function]: App\Console\Commands\SiteMap\SiteMapBuild->fire()
#1 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Container/Container.php(503): call_user_func_array(Array, Array)
#2 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Console/Command.php(150): Illuminate\Container\Container->call(Array)
#3 /Users/robberphex/projects/xxx/vendor/symfony/console/Command/Command.php(256): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#4 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Console/Command.php(136): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#5 /Users/robberphex/projects/xxx/vendor/symfony/console/Application.php(838): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#6 /Users/robberphex/projects/xxx/vendor/symfony/console/Application.php(189): Symfony\Component\Console\Application->doRunCommand(Object(App\Console\Commands\SiteMap\SiteMapBuild), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#7 /Users/robberphex/projects/xxx/vendor/symfony/console/Application.php(120): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 /Users/robberphex/projects/xxx/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(107): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 /Users/robberphex/projects/xxx/artisan(36): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 {main} {"request":[],}

可以看到,Exception都输出在同一个错误信息中了,方便排查错误,了解错误之间的关联。

Leave a Reply

Your email address will not be published. Required fields are marked *