hyperf中使用sentry

sentry文档:php

https://docs.sentry.io/swoole

在熟悉了hyperf架构异常捕捉机制以后,以为sentry能够以很是合理的方式使用在异常捕捉机制中。架构

首先安装sentry:app

composer require sentry/sdk:2.0.3

安装完后在hyperf.php文件中添加composer

Sentry\init(\['dsn' => 'your dsn']);

在App\Exception\Handle\AppExceptionHandle中curl

use Sentry;
public function handle(Throwable $throwable, ResponseInterface $response)
{
    Sentry\captureException($throwable);
    return $response->withStatus(500)->withBody(new SwooleStream('Internal Server Error.'));
}

这时候捕捉异常发现,sentry服务端并无接收到异常。查看源码发现,sentry的发送机制是在于一次请求结束后才清空信息队列,而hyperf是基于swoole的架构, 一次请求以后,并不会释放进程,因此sentry服务端没法接收到数据,Sentry\Transport\HttpTransport构造方法中的第三个参数解释为:异步

This flag controls whether to delay sending of the events until the shutdown of the application

默认为true,推送异常后sentry 并不会第一时间就把异常推送到服务端去而将会等到进程结束后才发送异常。 而init方法在sentry\ClientBuilder->createTransportInstance方法中建立的HttpTransport的第三个参数为true,修改成false再次捕捉异常即可以接收到请求了。性能

可是这时随之而来又一个问题,sentry的推送机制使用的是curl库,而swoole并不支持curl协程化,也就意味着,若是在生产环境中,同时出现大量的异常,会致使进程阻塞,hyperf的性能将严重降低。ui

异步处理刻不容缓!

再查看了文档以后,curl推送是经过Transport类来处理的,而默认的Transport是HttpTransport也就是同步推送机制,同时也提供了异步Transport, spoolTransport。url

删除hyperf.php的Sentryinit方法
在App\Exception\Handle\AppExceptionHandle中修改成以下

use Sentry\ClientBuilder;
use Sentry\Transport\SpoolTransport;
use Sentry\Transport\HttpTransport;
use Sentry\State\Hub;
use Sentry\Spool\MemorySpool;
use Http\Discovery\HttpAsyncClientDiscovery;
use Http\Discovery\MessageFactoryDiscovery;
use Sentry\Options;

public function handle(Throwable $throwable, ResponseInterface $response)
{
    // 这里为了方便在每次捕捉异常的时候都会从新初始化一次相关对象,应该全局单例保存起来
    $options = ['dsn' => 'https://<key>@sentry.io/<project>'\];
    $optionObj = new Options($options);
    $spool = new MemorySpool();
    $transport = new SpoolTransport($spool);
    $httpTransport = new HttpTransport($optionObj, HttpAsyncClientDiscovery::find(), MessageFactoryDiscovery::find());
    $builder = ClientBuilder::create($options);
    $builder->setTransport($transport);
    Hub::getCurrent()->bindClient($builder->getClient());
    Hub::getCurrent()->captureException($throwable);
    // 调用这个方法就会开始清空以前捕捉异常的队列,思考以后觉的放在定时任务里定时清空队列比较合理。
    $spool->flushQueue($httpTransport);
}

开始捕捉异常,怎么sentry服务端没有接收到请求,留下了没有技术的泪水。

排查问题后发现是由于HttpAsyncClientDiscovery::find()建立的异步httpTransport的url配置问题,仔细看了一遍源码后,发现可使用ClientBuilder->createTransportInstance方法,该方法默认是private,将其修改成public即可以使用它建立transport对象(ps 若是能够的话能够本身模仿写一个而不修改源码)。修改后的代码以下所示

App\Exception\Handle\AppExceptionHandle以下所示

use Sentry\ClientBuilder;
use Sentry\Transport\SpoolTransport;
use Sentry\State\Hub;
use Sentry\Spool\MemorySpool;

public function handle(Throwable $throwable, ResponseInterface $response)
{
    // 这里为了方便在每次捕捉异常的时候都会从新初始化一次相关对象,应该全局单例保存起来
    $options = ['dsn' => 'https://<key>@sentry.io/<project>'\];
    $spool = new MemorySpool();
    $transport = new SpoolTransport($spool);
    $builder = ClientBuilder::create($options);
    $httpTransport = $builder->createTransportInstance();
    $builder->setTransport($transport);
    Hub::getCurrent()->bindClient($builder->getClient());
    Hub::getCurrent()->captureException($throwable);
    // 调用这个方法就会开始清空以前捕捉异常的队列,思考以后觉的放在定时任务里定时清空队列比较合理。
    $spool->flushQueue($httpTransport);
}

捕捉异常,sentry服务端接收到异常。 在这里$spool->flushQueue($httpTransport); 将会主动推送捕捉的队列异常,思考以后觉的放在定时任务里定时清空队列比较合理。

相关文章
相关标签/搜索