Using sentry in hyperf

sentry document:

https://docs.sentry.io/

After being familiar with the exception catching mechanism of the hyperf architecture, I think sentry can be used in the exception catching mechanism in a very reasonable way.

First install sentry:

composer require sentry/sdk:2.0.3

Add in the hyperf.php file after installation

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

In App\Exception\Handle\AppExceptionHandle

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

At this time, we catch the exception and find that the sentry server does not receive the exception. Looking at the source code, it is found that the sending mechanism of Sentry is to clear the information queue after the process ends, while the hyperf is based on the swoole architecture. After a request, the process will not be released, so the sentry server cannot receive data. The third parameter in the Sentry\Transport\HttpTransport construction method is explained as follows:

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

The default value is true. Sentry will not push the exception to the server in the first time after pushing the exception, but will wait until the end of the process before sending the exception. The third parameter of HttpTransport created by init method in sentry \ clientbuilder - > createtransportinstance method is true, and the request can be received by changing it to false.

But then there is another problem. The push mechanism of sentry uses curl library, while swoole does not support curl co programming, which means that if a large number of exceptions occur in the production environment at the same time, the process will be blocked, and the performance of hyperf will be seriously reduced.

Asynchronous processing is urgent!

After reviewing the document again, curl push is processed through the Transport class. The default Transport is HttpTransport, which is also known as synchronous push mechanism. At the same time, asynchronous Transport and spooltransport are also provided.

Delete Sentryinit method of hyperf.php
In App\Exception\Handle\AppExceptionHandle, change to the following

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);
    // Calling this method will start to clear the queue that caught the exception before. After thinking about it, it is reasonable to put it in the timed task to clear the queue regularly.
    $spool->flushQueue($httpTransport);
}

Start to catch exceptions. How can the sentry server not receive the request, leaving tears without technology.

After troubleshooting, it is found that the reason is the url configuration problem of the asynchronous httpTransport created by httapasyncclientdiscovery:: find(). After a careful look at the source code, it is found that the clientbuilder > createtransportinstance method can be used. The default method is private, which can be used to create transport objects by modifying it to public. The modified code is as follows

App\Exception\Handle\AppExceptionHandle is as follows


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);
    // Calling this method will start to clear the queue that caught the exception before. After thinking about it, it is reasonable to put it in the timed task to clear the queue regularly.
    $spool->flushQueue($httpTransport);
}

Catch the exception. The sentry server receives the exception Here, $spool - > flushqueue ($httptransport); it will actively push the caught queue exception. After thinking about it, it's reasonable to put it in the timing task to clear the queue regularly.

Tags: PHP curl SDK Programming

Posted on Sun, 10 Nov 2019 14:03:04 -0500 by homchz