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); 將會主動推送捕捉的隊列異常,思考以後覺的放在定時任務裏定時清空隊列比較合理。