ThinkPHP日志级别配置在config/log.php中修改,通过channels下各通道的level数组控制接收的日志级别,如开发环境用env('APP_DEBUG')动态设置level为['debug','info','warning','error'],生产环境则仅保留['warning','error']。

ThinkPHP 的日志级别配置在哪改?
ThinkPHP 默认只记录 error 和 notice 级别日志(v6.0+ 默认为 error、warning、info),但你不能直接在 config/log.php 里写死一个新级别名来“新增级别”——日志级别是预定义的整数常量,比如 Log::DEBUG 对应 100,Log::INFO 是 200。真正起作用的是「日志通道的 level 配置」,它控制该通道**接收哪些级别以上的日志**。
所以关键不是“自定义级别”,而是:让某个通道按需开启/关闭特定级别日志的写入。
-
level配置值必须是数组,例如['debug', 'info', 'warning'],不能写成字符串'debug' - 如果设为
['error'],那Log::info('xxx')就不会被写入该通道,哪怕调用成功也不会落地 - 多个通道可共存,比如「文件通道只收 error」+「Sentry 通道收 warning 及以上」
如何让 debug 日志只在开发环境写入?
最常见需求:本地调试打 Log::debug(),上线后自动屏蔽,不靠手动删代码。这不是靠 if 判断,而是靠通道级 level 动态控制。
在 config/log.php 中,把 default 通道或自定义通道的 level 改为条件表达式:
立即学习“PHP免费学习笔记(深入)”;
return [
'default' => 'file',
'channels' => [
'file' => [
'type' => 'File',
'level' => env('APP_DEBUG', true) ? ['debug', 'info', 'warning', 'error'] : ['warning', 'error'],
'path' => runtime_path('log') . DIRECTORY_SEPARATOR,
],
],
];
-
env('APP_DEBUG')是 ThinkPHP 内置的环境判断方式,比硬编码更可靠 - 注意:修改后需清空
runtime/log/目录下的日志文件和runtime/cache/缓存,否则旧配置可能仍生效 - 不要用
Log::setLevel()运行时调整——它只影响默认通道,且对已初始化的通道无效
为什么 Log::debug() 没输出,但 Log::info() 可以?
大概率是当前通道的 level 配置没包含 debug。ThinkPHP 日志采用“通道过滤”机制:每条日志先按级别数值比较,再决定是否交由该通道处理。而 debug 级别最低(100),最容易被挡在外面。
- 检查
config/log.php中对应通道的level数组是否含'debug' - 确认没在中间件或全局事件中调用过
Log::init()覆盖了配置 - 运行
php think optimize:config后,配置会被合并缓存,此时改log.php不生效,必须删runtime/cache/下的配置缓存文件 - 使用
Log::getLevel()可查当前默认通道实际生效的级别列表(返回索引数组)
想按模块/控制器单独记日志,怎么配通道?
ThinkPHP 支持多通道并行,但不内置“按命名空间自动路由”。你需要显式指定通道名,并在业务代码中调用对应通道。
例如,在 config/log.php 增加一个专用于支付模块的通道:
'pay' => [
'type' => 'File',
'level' => ['info', 'warning', 'error'],
'path' => runtime_path('log/pay/') . DIRECTORY_SEPARATOR,
],
然后在支付逻辑里写:
Log::channel('pay')->info('order created', ['order_id' => $id]);
- 通道名(如
'pay')必须和配置键一致,区分大小写 - 每个通道独立维护自己的
level、path、max_files等,互不影响 - 避免通道名用数字开头或含特殊字符,否则
Log::channel()调用会报错 - 如果频繁切换通道,建议封装一个简单的日志门面类,而不是到处写
Log::channel(...)
level 数组决定“收什么”,Log::channel() 决定“往哪写”。别试图覆盖底层日志级别常量,也别依赖运行时 setLevel —— 那些都是障眼法。真正要动的,只有配置里的那个数组。



















