如何在Laravel中全局修改Saloon/Guzzle以捕获500错误上报Sentry?
全局捕获Saloon请求500状态码并上报Sentry的方案
针对你的需求,无需逐个修改20余个服务的请求代码,以下两种优雅的全局实现方式可以解决问题:
方案一:使用Saloon全局请求中间件(推荐)
Saloon原生支持请求/响应中间件,通过全局注册中间件,可拦截所有Saloon请求的响应,自动检查状态码并上报。
步骤1:创建自定义中间件
在项目中新建中间件文件 app/Saloon/Middlewares/ReportServerErrorMiddleware.php:
<?php namespace App\Saloon\Middlewares; use Saloon\Contracts\Middleware; use Saloon\Contracts\Request; use Saloon\Contracts\Response; use Exception; class ReportServerErrorMiddleware implements Middleware { // 请求发送前无需处理,留空即可 public function handleRequest(Request $request): void {} public function handleResponse(Response $response): void { // 检查响应状态码是否为500及以上 if ($response->status() >= 500) { // 构造异常,包含请求信息便于排查 $exception = new Exception( sprintf( '外部API服务器错误: %s %s 返回状态码 %d', $response->getRequest()->getMethod(), $response->getRequest()->getUrl(), $response->status() ), $response->status() ); // 给Sentry附加请求/响应详情,方便定位问题 app('sentry')->configureScope(function (\Sentry\State\Scope $scope) use ($response) { $scope->setContext('external_api_request', [ 'method' => $response->getRequest()->getMethod(), 'url' => $response->getRequest()->getUrl(), 'headers' => $response->getRequest()->headers()->all(), 'body' => $response->getRequest()->body()->all(), ]); $scope->setContext('external_api_response', [ 'status' => $response->status(), 'headers' => $response->headers()->all(), 'body' => $response->body(), ]); }); // 触发Laravel的异常上报,自动走你已配置的Sentry逻辑 report($exception); } } }
步骤2:全局注册中间件
在 app/Providers/AppServiceProvider.php 的 boot 方法中注册该中间件:
use Saloon\Facades\Saloon; use App\Saloon\Middlewares\ReportServerErrorMiddleware; public function boot() { // 注册Saloon全局中间件 Saloon::globalMiddleware([ ReportServerErrorMiddleware::class, ]); }
方案二:使用Guzzle底层中间件
如果希望从Guzzle层面拦截所有HTTP请求(包括非Saloon发起的Guzzle请求),可以通过Guzzle的HandlerStack添加中间件。
步骤1:创建Guzzle中间件
新建文件 app/Guzzle/Middlewares/ReportServerErrorMiddleware.php:
<?php namespace App\Guzzle\Middlewares; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Exception; class ReportServerErrorMiddleware { public function __invoke(callable $handler) { return function (RequestInterface $request, array $options) use ($handler) { return $handler($request, $options)->then(function (ResponseInterface $response) use ($request) { if ($response->getStatusCode() >= 500) { $exception = new Exception( sprintf( '外部API服务器错误: %s %s 返回状态码 %d', $request->getMethod(), $request->getUri()->__toString(), $response->getStatusCode() ), $response->getStatusCode() ); // 附加请求/响应上下文到Sentry app('sentry')->configureScope(function (\Sentry\State\Scope $scope) use ($request, $response) { $scope->setContext('external_api_request', [ 'method' => $request->getMethod(), 'url' => $request->getUri()->__toString(), 'headers' => $request->getHeaders(), 'body' => (string)$request->getBody(), ]); $scope->setContext('external_api_response', [ 'status' => $response->getStatusCode(), 'headers' => $response->getHeaders(), 'body' => (string)$response->getBody(), ]); }); report($exception); } return $response; }); }; } }
步骤2:全局配置Saloon使用该中间件
同样在 AppServiceProvider 的 boot 方法中添加:
use Saloon\Facades\Saloon; use App\Guzzle\Middlewares\ReportServerErrorMiddleware; public function boot() { // 给所有Saloon请求添加Guzzle中间件 Saloon::globalRequestMiddleware(function ($request) { $request->withMiddleware([ new ReportServerErrorMiddleware(), ]); }); }
方案对比
- Saloon中间件:更贴合Saloon生态,直接操作Saloon的Request/Response对象,无需转换格式,仅对Saloon请求生效,推荐使用。
- Guzzle中间件:从底层拦截所有Guzzle请求,适合项目中存在其他非Saloon的Guzzle请求场景,覆盖范围更广。
配置完成后,所有Saloon请求返回500+状态码时都会自动触发Sentry告警,后续新增的服务也会自动生效,无需逐个修改请求代码。
内容的提问来源于stack exchange,提问作者DeveloperX
相关产品推荐
相关产品推荐

