PHP SDK не отправляет ошибки в Sentry при вызове из IBM Cloud Functions - PullRequest
0 голосов
/ 23 апреля 2019

Я использую Serverless Framework для развертывания своего PHP-кода в качестве IBM Cloud Function.

Вот код из файла действия PHP:

function main($args): array {

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

    try {
        throw new \Exception('Some error')
    } catch (\Throwable $exception) {
        Sentry\captureException($exception);
    }
}

А это файл serverless.yml:

service: cloudfunc

provider:
  name: openwhisk
  runtime: php

package:
  individually: true
  exclude:
    - "**"
  include:
    - "vendor/**"

functions:
    test-sentry:
    handler: actions/test-sentry.main
    annotations:
        raw-http: true
    events:
        - http:
            path: /test-sentry
            method: post
            resp: http
    package:
        include:
        - actions/test-sentry.php

plugins:
  - serverless-openwhisk

Когда я тестирую обработчик действий из своей локальной среды (контейнеры NGINX / PHP Docker), ошибки отправляются в Sentry.

Но когда я пытаюсь вызвать действие из IBM Cloud, в консоли Sentry ничего не появляется.

Edit:

Через некоторое время, пытаясь выяснить источник проблемы, я обнаружил, что она связана с асинхронной природой отправки http-запроса в Sentry (у меня есть другие библиотеки, которые устанавливают HTTP / TCP-соединения с Loggly, RabbitMQ, MySQL, и все они работа как положено):

vendor/sentry/sentry/src/Transport/HttpTransport.php

в методе send, куда отправляется фактический http-запрос:

public function send(Event $event): ?string
    {
        $request = $this->requestFactory->createRequest(
            'POST',
            sprintf('/api/%d/store/', $this->config->getProjectId()),
            ['Content-Type' => 'application/json'],
            JSON::encode($event)
        );

        $promise = $this->httpClient->sendAsyncRequest($request);

        //The promise state here is "pending"
        //This line here is being logged in the stdout of the invoked action
        var_dump($promise->getState());

        // This function is defined in-line so it doesn't show up for type-hinting
        $cleanupPromiseCallback = function ($responseOrException) use ($promise) {

            //The promise state here is "fulfilled"
            //This line here is never logged in the stdout of the invoked action
            //Like the execution never happens here
            var_dump($promise->getState());

            $index = array_search($promise, $this->pendingRequests, true);

            if (false !== $index) {
                unset($this->pendingRequests[$index]);
            }

            return $responseOrException;
        };

        $promise->then($cleanupPromiseCallback, $cleanupPromiseCallback);

        $this->pendingRequests[] = $promise;

        return $event->getId();
    }

Ответы [ 2 ]

3 голосов
/ 23 апреля 2019

Запросы, которые регистрируются асинхронно, отправляются в деструкторе экземпляра HttpTransport или когда PHP выключается при регистрации функции выключения. В OpenWhisk мы никогда не выключаемся, поскольку выполняем бесконечный цикл, пока контейнер Docker не будет уничтожен.

В результате, чтобы сделать эту работу, нам нужно вызвать деструктор свойства $transport Hub * $client. К сожалению, это закрытый, поэтому самый простой способ сделать это - использовать отражение, чтобы сделать его видимым, а затем вызвать его:

$client = Sentry\State\Hub::getCurrent()->getClient();
$property = (new ReflectionObject($client))->getProperty('transport');
$property->setAccessible(true);
$transport = $property->getValue($client);
$transport->__destruct();

Это сделает свойство $transport видимым, чтобы мы могли получить его и вызвать деструктор, который, в свою очередь, вызовет cleanupPendingRequests(), который затем отправит запросы на sentry.io.

Поэтому main() выглядит так:

function main($args): array {

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

    try {
        throw new \Exception('Some error')
    } catch (\Throwable $exception) {
        Sentry\captureException($exception);
    }

    $client = Sentry\State\Hub::getCurrent()->getClient();
    $property = (new ReflectionObject($client))->getProperty('transport');
    $property->setAccessible(true);
    $transport = $property->getValue($client);
    $transport->__destruct();

    return [
        'body' => ['result' => 'ok']
    ];
} 

Кстати, мне интересно, работает ли этот Sentry SDK со Swoole?

2 голосов
/ 23 апреля 2019

Время выполнения функции «приостанавливается» между запросами платформы.Это означает, что любые фоновые процессы будут заблокированы, если они не будут завершены, когда функция вернется.

Похоже, что асинхронный HTTP-запрос не может завершиться до паузы во время выполнения.

Вам нужно будет найти способ заблокировать возврат из функции до завершения этого запроса.,Если в Sentry SDK есть какой-то обработчик обратного вызова или другой механизм, который должен быть уведомлен о том, что сообщения отправлены, вы могли бы это использовать?

...