ThinkPHP 6+多应用模式下exception_handle需按应用单独配置:每个子应用必须有app/{name}/ExceptionHandle.php,命名空间严格匹配路径,继承\think\exception\Handle,render()中通过$request->app_name()识别应用并差异化响应,日志需手动隔离到runtime/log/{app}/目录。

多应用下 exception_handle 配置为什么无效
直接在根目录 config/app.php 里设 'exception_handle' => app\exception\ExceptionHandler::class,对 admin、api 等子应用完全不生效——框架只加载当前绑定应用的配置文件,config/app.php 是全局入口配置,不参与应用级异常处理器的加载链。
每个应用必须有自己的 ExceptionHandle.php
ThinkPHP 6+ 多应用模式下,异常处理器按应用隔离,必须为每个子应用单独提供 app/{name}/ExceptionHandle.php 文件:
- 路径必须是
app/admin/ExceptionHandle.php,不是app/ExceptionHandle.php或app/admin/exception/ExceptionHandler.php - 类名必须为
app\admin\ExceptionHandle(命名空间严格匹配应用路径) - 必须继承
\think\exception\Handle,且render()方法需显式返回\think\Response实例 - 文件不能为空、不能只有注释、不能含
echo/var_dump,否则框架加载失败后 fallback 到默认处理
如何在 render() 中识别当前应用并差异化处理
不能依赖全局配置或硬编码判断,必须从请求对象中实时提取应用名:
- 先确保已定义应用请求类(如
app/Request.php),其中app_name()方法返回当前应用名 - 在
app/admin/ExceptionHandle.php的render()方法中调用$request->app_name()获取值,再做分支处理 - 例如 admin 应用返回 JSON,web 应用渲染 HTML 模板,避免所有应用共用同一套响应格式
- 注意:不要在
render()里调用view()或json()辅助函数,它们可能未初始化;应直接 new\think\Response或用response()->code(500)->content(...)
容易被忽略的 runtime 和日志隔离问题
即使异常处理器分开了,日志仍可能混写到同一个文件:
立即学习“PHP免费学习笔记(深入)”;
-
LOG_PATH默认指向runtime/log/,所有应用共享该路径 → 错误日志内容交叉不可追溯 - 解决办法:在每个应用的
ExceptionHandle.php中,手动拼接应用专属日志路径,例如:log('admin_error', $e->getMessage(), 'admin'); - 更稳妥做法:重写
report()方法,把异常信息写入runtime/log/admin/子目录,需提前创建该目录并确保可写 - 若使用
env('APP_DEBUG', false)控制调试开关,注意它读取的是根目录.env,不是各应用自己的配置 —— 调试开关本身无法按应用独立



















