报错信息像摩斯电码?我按"症状-真凶-秒查"整理了一份速查备忘录

小助手
小助手 版主圣羽星庭 勋望元宿志愿先锋
社区管理
站长杂谈 125 浏览 0 回复

昨晚三点被监控告警炸醒,生产环境抛了个 Undefined index: access_token,我盯着这行字愣了半分钟——明明本地跑得好好的。后来才想起是微信授权回调里少了一层 isset,但最气人的不是这个,是我花了二十分钟才确认问题不在 Redis、不在队列、不在别人改的配置,就在自己上周"临时"写的那个 if 分支里。

这种事多了,我慢慢养出个习惯:遇到 Exception 不急着搜 Stack Overflow,先按报错形态归类。下面这几类是我这两年踩出来、也最容易让人绕远的:

一、"类不见了"系列:autoload 在跟你玩捉迷藏

Class 'App\Service\OrderFactory' not found 这种,十个里有六个不是类真丢了。我的排查顺序现在是:

1. 先看文件名和类名大小写——Linux 下 Orderfactory.phpOrderFactory.php 是两码事,本地 Windows/Mac 可能蒙混过关
2. 再扫一眼 namespace,尤其从别的项目 copy 过来的 Service 层,namespace 还挂着老前缀
3. 最后才怀疑 composer autoload,执行 composer dump-autoload 之前,先确认 composer.json 里的 psr-4 映射没被人手滑改错

上个月就栽过一次:同事把 app/admin/service 改成了 app/admin/services,目录里多了一个 s,IDE 不报错,上线直接炸。

二、"属性不存在"系列:模型里的幽灵字段

ThinkPHP 的 Property not exist 我熟,但最阴的是那种"时有时无"的。后来总结出个规律:

- 报错带具体字段名(如 ->status_text)→ 检查模型是否用了 getStatusTextAttr 读取器,或者字段是不是在 hidden 数组里
- 报错是通用 Property not exist: relation → 大概率是 with 预加载时关联名写错,或者模型里 belongsTo 的第二个参数表名不对
- 生产环境独有、本地正常 → 九成九是缓存,php think cache:clear 先打一套

有个邪门的:模型里用了 $append 追加虚拟字段,但读取器里又抛了异常,外层抓到的却是 Property not exist,真正的错被吞在底层。

三、"SQL 语法错误"系列:复制粘贴的代价

SQLSTATE[42000]: Syntax error or access violation 这种,我现在的第一反应不是看 SQL 语句,而是看最近谁动过数据库配置

真实案例:测试环境 MySQL 5.7,生产是 8.0,某个查询用了 GROUP BY 但没把 select 里的非聚合字段写全。5.7 的 sql_mode 默认宽松,8.0 严格模式直接拒。报错位置指向的往往是语法"看起来正常"的那行。

还有更隐蔽的:ORM 链式调用里某个 where 条件传了数组,但键名写错,生成的 SQL 里多了个 Array 字符串,报错位置却指在完全不相干的行。

四、"连接失败"系列:别急着骂网络

Redis、MySQL、OSS 连不上,我现在先查三个地方,顺序不能乱:

1. .env 里是不是还挂着去年迁移前的内网 IP
2. 宝塔/服务器的安全组,尤其阿里云经典网络切 VPC 那次,内网地址段变了但安全组规则没跟
3. 最后才是服务本身:MySQL 的 max_connections、Redis 的 timeout、OSS 的 Bucket 是不是切了地域

最冤的一次:Connection refused 折腾半小时,发现是 Docker 容器重启后 IP 变了,而配置文件里写死了 172.17.0.3。

五、我现在的"第一眼"习惯

报错信息往下滚,先看最后一个 caused by,再看文件路径里有没有 vendor——如果是框架底层抛的,往上追两帧一般就到自己代码了。如果是业务代码直接抛的,反而要怀疑是不是框架某层把原始异常包了一层。

还有个小技巧:TP6 开 APP_DEBUG 时,异常页面的"数据"标签页经常藏着真正的 SQL 或请求参数,比看 trace 堆栈快。

你们有没有那种"报错只有一行,排查一整晚"的经历?我这份备忘录还在补,最近想加上 Swoole 协程环境下的异常捕获——那又是另一个故事了。

评论0
回复 · 0
还没有回复
微信客服 微信客服