thinkphp 6 的 http 类请求失败时默认不记录完整错误详情,需手动捕获 requestexception 等异常并结构化记录请求、响应及脱敏后敏感信息。

ThinkPHP 6 的 Http 类请求失败时默认不记录错误详情
直接用 think\facade\Http::get() 或 post() 调第三方接口,一旦网络超时、SSL 验证失败、返回非 2xx 状态码,Http 类默认只抛出异常,但不会把原始响应体、请求头、错误码等写入日志。你看到的可能只是 ClientException 或 ServerException,没有上下文,根本没法定位是对方挂了、证书过期,还是你传了非法参数。
手动捕获异常并记录完整请求/响应信息
必须在 try...catch 中显式提取关键字段,不能只打 $e->getMessage()。尤其注意:异常对象本身可能不含响应体(比如 DNS 失败时根本没发出去),所以得区分“请求发出前”和“请求发出后”的错误类型。
-
think\exception\HttpException和GuzzleHttp\Exception\RequestException是常见父类,优先 catch 这两个 - 用
$e->getRequest()取原始Request对象,调getMethod()、getUri()、getBody()->getContents()(注意:body 可能已读取过,需提前保存) - 用
$e->getResponse()取响应(仅当请求已发出且有响应时存在),再取getStatusCode()、getReasonPhrase()、getBody()->__toString() - 日志建议用
Log::error(),内容结构化,例如:"[http] req: GET /api/v1/user, status: 0, msg: cURL error 7: Failed to connect"
推荐封装一个带日志的 HTTP 客户端
别每次调第三方都手写 try/catch。在 app/common/HttpLogger.php 里写个静态方法,统一处理请求、记录、重试(可选)。关键是把 body 提前缓存——Guzzle 的 Stream 是单次读取的,不缓存就丢了。
use think\facade\Log;
use think\facade\Http;
use GuzzleHttp\Exception\RequestException;
class HttpLogger
{
public static function request($method, $url, $options = [])
{
try {
// 缓存原始 body,防止后续无法读取
$body = $options['body'] ?? null;
if (is_string($body)) {
Log::info("[http] req: {$method} {$url}, body: " . substr($body, 0, 500));
}
return Http::{$method}($url, $options);
} catch (RequestException $e) {
$req = $e->getRequest();
$res = $e->getResponse() ?: null;
$status = $res ? $res->getStatusCode() : 0;
$msg = $e->getMessage();
Log::error("[http] req: {$method} {$url}, status: {$status}, msg: {$msg}");
if ($res) {
Log::error("[http] res body: " . substr((string) $res->getBody(), 0, 1000));
}
throw $e;
}
}
}
日志文件权限和敏感信息过滤要提前检查
线上环境如果日志目录不可写,或者 log.level 设成 warning,那 error() 日志也写不进去。另外,第三方接口常带 token、手机号、身份证号,直接打全量 body 会违规。必须做脱敏:
- 对 URL 中的 query 参数,用
parse_url($url, PHP_URL_QUERY)解析后过滤token、sign等键 - 对 JSON body,先
json_decode($body, true),再用array_walk_recursive清洗敏感字段值(如替换为***) - 避免在日志里记录
Authorization请求头,可用正则匹配并替换:preg_replace('/Authorization:\s*[^\r\n]+/', 'Authorization: ***', $headers)
真正难的不是记日志,是记对日志——既要足够还原现场,又不能泄露凭据或用户数据。少一个 substr() 截断,就可能把整张银行卡号打到磁盘上。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











