调试模式日志全量即时记录含敏感信息,生产日志默认关闭、需显式配置级别且仅记关键事件;日志开关(log.level)与调试模式(APP_DEBUG)独立控制,DB_DEBUG开启可能泄露表结构。

调试模式日志和生产日志不是“内容不同”,而是“是否写入”和“写什么”的逻辑完全不同。
调试模式日志:全量、即时、带敏感信息
当 APP_DEBUG = true 时,ThinkPHP6 会自动激活完整调试链路:
- 所有错误、警告、Notice、SQL执行语句、异常堆栈都会被记录(包括
Runtime/Logs/24_10_15.log这类按日期命名的文件) -
Log::record()调用立即生效,不排队;exception抛出时堆栈直接输出到页面或日志 - SQL日志可能含真实参数值(如
whereRaw('name = "'.$input.'"')拼接后的完整语句),暴露表结构、字段名甚至变量内容 - 日志路径可被外部直接访问(如
/runtime/logs/2024_10_15.log),若服务器配置疏忽,等于把数据库密码、API密钥等白送出去
生产环境日志:默认关闭、需显式配置、只记关键事件
当 APP_DEBUG = false(即生产环境),框架主动跳过日志写入逻辑,哪怕 runtime/log 目录存在且权限正确,里面也始终为空。
- 必须在
config/app.php中明确配置'log' => ['level' => ['error', 'sql']],仅设'type' => 'File'不生效 - 日志默认延迟写入——请求结束时统一刷盘;中间调用
Log::record()只是入队,不会立刻生成文件 - 若要强制落盘(比如记录支付失败这种关键动作),得手动调用
Log::save() - SQL日志仅记录慢查询或报错SQL(取决于
db.sql_explain和log.level配置),不记录正常查询的完整语句
一个容易忽略的关键事实:日志开关和日志内容是两回事
很多人以为“开了日志驱动就等于有日志”,其实不是:
立即学习“PHP免费学习笔记(深入)”;
- 没开
log.level,即使APP_DEBUG = true,某些自定义日志也不会触发 - 开了
log.level但APP_DEBUG = false,SQL日志仍可能被记录(取决于是否启用DB_DEBUG),但异常堆栈绝不会写入文件 -
DB_DEBUG = true且生产环境未关闭,buildSql()生成的SQL若进日志,会连带泄露表结构——这比SQL注入本身更早暴露攻击面



















