optimus/heimdal

3.0.4 2023-12-28 14:46 UTC

This package is auto-updated.

Last update: 2024-08-28 16:13:20 UTC


README

Latest Version Software License Build Status Coverage Status Total Downloads

简介

Heimdal 是一个专门为 API 构建而设计的 Laravel 异常处理程序。

为什么需要它?

在构建 API 时,有特定的格式规范来决定如何将错误信息发送给用户。像 Laravel 这样的框架并非专门为 API 构建者设计。这个小库正好填补了这一空白。例如,像 JSON API 这样的规范有 关于错误格式化的指南

安装

composer require optimus/heimdal ~1.0

将服务提供者添加到 config/app.php

// other providers...
Optimus\Heimdal\Provider\LaravelServiceProvider::class,

发布配置。

php artisan vendor:publish --provider="Optimus\Heimdal\Provider\LaravelServiceProvider"

将异常处理程序添加到 bootstrap/app.php

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    Optimus\Heimdal\ExceptionHandler::class
);

它做什么?

假设你有一段代码抛出了 InvalidArgumentException。这是一个服务器错误(500)。它将按照以下描述的流程进行解析。

1. Exception is thrown
2. The Exception is parsed through reports. A reporter is a class that reports the Exception. Log it in logs, send to external trackers like Sentry, Bugsnag etc.
3. The Exception is parsed through an appropriate formatter that formats the response in accordance to the error type.
4. The response is sent to the user.

这难道不是 Laravel 做的吗?

是的,基本上是这样。然而,如果你想向像 Sentry 这样的服务报告错误,通常你会通过像 Monolog 这样的工具来这样做。Monolog 的问题是难以 获取报告者的响应。例如,Sentry 为每个报告的异常返回一个唯一的 ID,这对于用户来说非常有用,因为他们可以将它提供给客户支持。Heimdal 通过将所有报告者的响应提供给格式化类来直接支持这一点,这使得格式化类可以使用报告者的响应来生成最终的响应给用户。

其次,Heimdal 提供了关于如何向用户报告不同错误类型的合理的默认值,并使实现特定异常类型的替代响应变得简单。

配置

Heimdal 有两样东西需要配置:格式化和报告者。

报告者

你应该确定异常应该报告到何处。Heimdal 仍然会调用 Laravel 的基本报告函数,所以你的异常仍然会按正常方式记录。但是,添加外部报告很容易。Heimdal 默认集成了 Sentry。要向 Sentry 发送异常,只需在 config/optimus.heimdal.phpreporters 部分添加此条目即可。

'sentry' => [
    'class'  => \Optimus\Heimdal\Reporters\SentryReporter::class,
    'config' => [
        'dsn' => '',
        // For extra options see https://docs.sentry.io/clients/php/config/
        // php version and environment are automatically added.
        'sentry_options' => []
    ]
]

添加自定义报告者,例如 Bugsnag,只需编写一个小的报告者类,例如

<?php

namespace My\Namespace\Exceptions\Reporters;

use Optimus\Heimdal\Reporters\ReporterInterface;

class BugsnagReporter implements ReporterInterface
{
    public function __construct(array $config)
    {
        // initialize with config
    }

    public function report(Exception $e)
    {
        // report to bugsnag
    }
}

然后将其添加到 config/optimus.heimdal.php

'bugsnag' => [
    'class'  => \My\Namespace\Exceptions\Reporters\BugsnagReporter::class,
    'config' => [
        // config.
    ]
]

格式化器

Heimdal 默认提供了一些合理的格式化器。在 config/optimus.heimdal.php 中定义了格式化器的优先级。

'formatters' => [
    SymfonyException\UnprocessableEntityHttpException::class => Formatters\UnprocessableEntityHttpExceptionFormatter::class,
    SymfonyException\HttpException::class => Formatters\HttpExceptionFormatter::class,
    Exception::class => Formatters\ExceptionFormatter::class,
],

条目越高,优先级越高。在这个例子中,一个 UnprocessableEntityHttpException 将使用 UnprocessableEntityHttpExceptionFormatter 进行格式化,因为它是最先的条目。然而,一个 NotFoundHttpException 不会匹配 UnprocessableEntityHttpException,但会匹配 HttpException(因为它是子类),因此将使用 HttpExceptionFormatter 进行格式化。

你可以轻松编写自定义格式化器

<?php

namespace My\Namespace\Exceptions\Formatters;

use Exception;
use Illuminate\Http\JsonResponse;
use Optimus\Heimdal\Formatters\BaseFormatter;

class NotFoundHttpExceptionFormatter extends BaseFormatter
{
    public function format(JsonResponse $response, Exception $e, array $reporterResponses)
    {
        $response->setStatusCode(404);
        $data = $response->getData(true);

        if ($this->debug) {
            $data = array_merge($data, [
                'code'   => $e->getCode(),
                'message'   => $e->getMessage(),
                'exception' => (string) $e,
                'line'   => $e->getLine(),
                'file'   => $e->getFile()
            ]);
        } else {
            $data['message'] = [
                'message' => 'The resource was not found.',
                'log_id' => $reporterResponses['sentry']['sentry_id']
            ]
        }

        $response->setData($data);
    }
}

注意我们如何轻松地使用 $reporterResponses 将 Sentry 日志的 ID 附加到 JSON 响应中。现在我们只需将其添加到 config/optimus.heimdal.php

'formatters' => [
    SymfonyException\UnprocessableEntityHttpException::class => Formatters\UnprocessableEntityHttpExceptionFormatter::class,
    SymfonyException\NotFoundHttpException::class => My\Namespace\Exceptions\Formatters\NotFoundHttpExceptionFormatter::class,
    SymfonyException\HttpException::class => Formatters\HttpExceptionFormatter::class,
    Exception::class => Formatters\ExceptionFormatter::class,
],

现在所有 NotFoundHttpException 都将使用我们的自定义格式化器进行格式化。

可用的报告者

Sentry

要将异常发送到Sentry,请在 config/optimus.heimdal.php 中添加以下报告器配置。

'reporters' => [
    'sentry' => [
        'class'  => \Optimus\Heimdal\Reporters\SentryReporter::class,
        'config' => [
            'dsn' => '',
            // For extra options see https://docs.sentry.io/clients/php/config/
            // php version and environment are automatically added.
            'sentry_options' => []
        ]
    ]
]

在运行时添加上下文

有时您需要在运行时添加信息,例如请求数据、用户信息等。为此,您可以将 add_context 键添加到 config 数组中。以下是如何实现的一个示例。

'reporters' => [
    'sentry' => [
        'class'  => \Optimus\Heimdal\Reporters\SentryReporter::class,
        'config' => [
            'dsn' => env('SENTRY_DSN'),
            // For extra options see https://docs.sentry.io/clients/php/config/
            // php version and environment are automatically added.
            'add_context' => function (Exception $e) {
                $context = [
                    'environment' => app()->environment(),
                    'release' => \Infrastructure\Version::getGitTag()
                ];

                $user = \Auth::User();
                if ($user) {
                    $context['user'] = [
                        'id' => $user->id,
                        'email' => $user->email,
                    ];
                } else {
                    $context['user'] = [];
                }

                // When running in console request is not available
                if (substr(php_sapi_name(), 0, 3) !== 'cli') {
                    $request = app('request');

                    if (!isset($context['extra'])) {
                        $context['extra'] = [];
                    }

                    $context['extra']['request_data'] = json_encode($request->all());
                    $context['user']['ip_address'] = \Request::getClientIp();
                }

                return $context;
            }
        ]
    ]
]

Bugsnag

感谢Nikolaj Løvenhardt Petersen添加了对它的支持

使用Laravel安装指南安装Bugsnag

要将异常发送到Bugsnag,请在 config/optimus.heimdal.php 中添加以下报告器配置。

'reporters' => [
    'bugsnag' => [
        'class'  => \Optimus\Heimdal\Reporters\BugsnagReporter::class,
        'config' => []
    ]
]

Rollbar

要将异常发送到Rollbar,请在 config/optimus.heimdal.php 中添加以下报告器配置。

'reporters' => [
'rollbar' => [
        'class'  => \Optimus\Heimdal\Reporters\RollbarReporter::class,
        'config' => [
            'access_token' => '',
            'environment' => 'production'
        ]
    ]
]

完整的配置选项列表可以在 此处 找到

标准

此包符合 PSR-1PSR-2PSR-4 标准。如果您发现符合标准的地方,请通过pull request发送补丁。

测试

$ phpunit

贡献

有关详细信息,请参阅 CONTRIBUTING

许可证

MIT许可证(MIT)。有关更多信息,请参阅 许可证文件