thinkphp 5/6 日志默认写入 runtime/log/ 目录,需目录可写且未禁用日志;仅触发 log::record()、异常或 app_debug=true 时生成;按天分文件(如 20240520.log);时间戳为服务器本地时区;常见失败原因为权限不足、磁盘满、swoole 多进程冲突等。

日志文件在哪?默认路径和生成条件
ThinkPHP 5/6 的 Runtime 日志默认写在 runtime/log/ 目录下,但这个目录必须可写,且应用未禁用日志('log' => ['enable' => false] 会直接跳过)。
不是所有请求都会写日志——只有触发了 Log::record() 或发生异常、调试开启(app_debug = true)时,才会落盘。
-
runtime/目录需由 Web 服务器用户(如 www-data、nginx)有写权限,否则日志静默失败 - 首次访问时若目录不存在,TP 会尝试自动创建;但若父目录不可写(比如
runtime所在磁盘满或挂载为只读),则完全不生成日志文件 - 日志按天分文件,如
runtime/log/20240520.log,不是单个大文件滚动
日志内容看不懂?格式字段含义与时间戳问题
默认日志每行以 [日期 时间] [级别] [模块] 内容 开头,例如:[2024-05-20 14:22:31] [error] [app] SQLSTATE[HY000] [2002] Connection refused。
- 方括号里的日期是 PHP
date('Y-m-d H:i:s')输出,不是 UTC,而是服务器本地时区;若服务器时区设错(如系统为 CST 但 PHP 误配为 UTC),会导致日志时间比实际晚/早 8 小时,排查时容易误判时间线 - 级别字段(
error、info、sql)取决于调用Log::error()或框架内部判断,sql日志需开启'log' => ['sql' => true] - 模块名(如
app、core)来自日志上下文,但自定义命令行任务中若没显式设置,可能显示为console或空
查不到日志?常见静默失败原因
日志“没生成”或“有内容但看不到”,大概率不是配置漏了,而是以下几处卡住了:
-
runtime/目录权限不对,Linux 下用ls -ld runtime看属主和权限,常见错误是属主为 root,Web 进程无法写入 -
log.max_files设得太小(如 10),旧日志被轮转删除,而你正好看的是已被删的日期文件 - 使用 Swoole 或 Workerman 时,
runtime/路径可能被多进程共享冲突,导致部分进程写失败(尤其在 reload 后) - TP6 中若启用了
think-swoole扩展,日志默认走 Swoole 的异步写,但若未正确安装swoole扩展,会退化为同步写,此时若opcache开启且未刷新,可能缓存了旧的 log 配置
想快速定位某次请求?用 trace_id 或手动加标识
TP 自身不带全局 trace_id,但你可以通过中间件在日志前缀里注入唯一标识:
// app/middleware/LogTrace.php
public function handle($request, \Closure $next)
{
$traceId = uniqid('req_', true);
\think\Log::setLevel(\think\Log::INFO);
\think\Log::info("REQ_START {$traceId} " . $request->url(true));
$response = $next($request);
\think\Log::info("REQ_END {$traceId}");
return $response;
}
- 这样查日志时 grep
req_就能串起一次完整请求链 - 注意不要在
Log::error()里拼接敏感参数(如密码、token),TP 不自动脱敏,日志文件可能被非授权访问 - 若用 Logstash 或 ELK 收集,建议提前把
runtime/log/挂载为独立卷,避免日志和代码混在一起被误删
日志格式看着简单,但时间、权限、进程模型这三块最容易让人查到凌晨还没定位到问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











