PHP 从 IBM Cloud Functions 调用时,SDK 不会向 Sentry 发送错误

PHP SDK not sending errors to Sentry when invoked from IBM Cloud Functions

我正在使用无服务器框架将我的 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 控制台中没有显示任何内容。

编辑:

经过一段时间尝试调查问题的根源后,我发现它与将 http 请求发送到 Sentry 的异步性质有关(我有其他库与 Loggly、RabbitMQ 建立 HTTP/TCP 连接, MySQL 并且它们都按预期工作):

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

在发送实际 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();
    }

函数运行时 "paused" 在平台请求之间。这意味着如果在函数 returns 时没有完成任何后台进程,它们将被阻止。

看起来异步 HTTP 请求在运行时暂停之前没有机会完成。

您需要找到一些方法来阻止函数返回,直到该请求完成。如果 Sentry SDK 有一些回调处理程序或其他机制在发送消息时得到通知,您可以使用它吗?

异步注册的请求在 HttpTransport 实例的析构函数中发送,或者在 PHP 关闭时发送,因为已注册关闭函数。在 OpenWhisk 中,我们永远不会关闭,因为我们 运行 在一个永无止境的循环中,直到 Docker 容器被杀死。

更新:您现在可以调用$client-flush(),无需担心反射。

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();
    $client->flush();

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

原解释:

因此,要使这项工作正常进行,我们需要调用 Hub$client$transport 属性 的析构函数。不幸的是,这个私有的,所以最简单的方法是使用反射使其可见然后调用它:

$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一起使用吗?