必须在app\exceptions\handler的render()方法中拦截异常并返回response实例,如modelnotfoundexception返回404、authorizationexception返回403;report()仅负责日志记录,不影响响应。

直接在 app/Exceptions/Handler.php 里改 report() 和 render() 就行,别碰 bootstrap/app.php 或全局 set_exception_handler() —— 那样会丢掉请求上下文、认证状态和日志 channel 绑定。
为什么 QueryException 日志里看不到真实 SQL 报错
Laravel 把 PDO 异常包装成 QueryException,原始错误藏在 $exception->getPrevious() 里。直接记 $exception->getMessage() 只能得到 “Integrity constraint violation”,完全没法排查。
- 必须在
Handler@report()中加类型判断:if ($exception instanceof \Illuminate\Database\QueryException) - 取真实错误:用
$exception->getPrevious()?->getMessage(),不是$exception->getMessage() - 敏感字段过滤:检查
$exception->getTraceAsString()是否含password、token、api_key,有就str_replace()掉再写日志 - 线上禁用
DB::enableQueryLog():它只适合本地调试,开启后会拖慢所有查询
中间件抛的异常为啥没进 Handler::report()
中间件执行在路由匹配之前,App\Exceptions\Handler 默认不接管这个阶段的异常。裸 throw new Exception() 会跳过框架异常流,直接冒泡成 500 页面,且可能不记录日志。
- 优先用 Laravel 标准方式:比如
abort(403)、throw new \Illuminate\Auth\Access\AuthorizationException() - 若必须自定义,确保类继承
Exception,并在Handler@render()里显式处理,例如: if ($exception instanceof MyAuthMiddlewareException) { return response()->json(['error' => 'Unauthorized'], 401); }- 路径前缀判断更可靠:中间件里
$request->expectsJson()可能不可用,建议用$request->is('api/*')区分 API/Web 请求
怎么让业务异常进日志但不触发告警
别一股脑把所有异常都往 Sentry 或 Slack 推。生产环境要分级:预期业务异常(如余额不足、库存超限)只记 Log::warning(),系统级故障(如 DB 连接失败、Redis 超时)才上报。
- 检查
$dontReport属性:别把InsufficientBalanceException加进去,否则它连日志都不写 - 避免重复上报:Laravel 默认已调用
parent::report($exception),你加逻辑前先确认是否真要绕过它 - 事务中异常必须暴露:在
DB::transaction()闭包里写try/catch并静默吞掉异常,事务就不会回滚 —— 这是线上数据不一致的常见源头 -
report()里调第三方服务(如 Slack)必须设超时:用Http::timeout(2)->post(...),否则异常处理器卡住会导致整个请求 hang 死
最易被忽略的是链式异常堆栈:Monolog 1.x 默认只打最外层异常的 trace,原始错误源会被掩盖。升级到 Monolog 2.x 是最省事的解法;如果卡在 1.x,就得换日志格式化器或手动遍历 $exception->getPrevious() 链补全 trace。











