开发阶段应禁用 JSON 日志,改用带颜色、行号、堆栈的纯文本日志以提升调试效率;FrankenPHP 默认已输出易读错误,无需结构化;需启用 cli_colors、Xdebug 及 REQUEST_ID 过滤,并注意 stderr/stdout 分流。

开发阶段别用 JSON 日志,直接关掉结构化输出,用带颜色和上下文的纯文本更高效
为什么开发时 JSON 日志反而拖慢调试
JSON 日志在生产环境利于采集和解析,但开发时每条日志都要 json_encode()、字段校验、时间格式转换,终端里看到的是压缩单行(如 {"level":"error","msg":"DB connect failed","file":"/app/db.php","line":42}),还得手动展开查字段。而你真正需要的是:一眼看出哪行报错、在哪、为什么错。
FrankenPHP 默认启动时(frankenphp serve)已经把 PHP 错误、警告、致命错误原样打到终端,带完整路径、行号、堆栈——这本身就是最易读的调试格式。强行套 JSON 只会掩盖关键信息。
- 别设
CADDY_LOG_OUTPUT=stdout(它会让 Caddy 访问日志也挤进 stdout,干扰 PHP 错误流) - 别在开发环境调用
error_log(json_encode(...), 0),除非你真在测日志采集链路 - 确保
display_errors = On和error_reporting = E_ALL在 php.ini 中生效(可用frankenphp php-cli -i | grep -E "(display_errors|error_reporting)"验证)
让终端日志带颜色和文件上下文
PHP 原生不支持彩色错误,但可通过 php.ini 启用 cli_colors = On(FrankenPHP 0.19+ 支持),并配合 xdebug.mode=develop 获得高亮堆栈和变量 dump。
立即学习“PHP免费学习笔记(深入)”;
若用 Xdebug(推荐 3.3+):
- 加配置
xdebug.file_link_format = "vscode://file/%f:%l",点击错误行直接跳转编辑器 - 在
php.ini中设xdebug.var_display_max_depth = 5,避免var_dump()折叠太深 - 禁用
xdebug.mode=off以外的模式(如profile或trace),它们会显著拖慢响应
没装 Xdebug?至少用 trigger_error("debug: " . print_r($data, true), E_USER_NOTICE) 替代裸 echo,这样错误级别和位置会被捕获并高亮。
快速过滤和定位某次请求的日志
开发时经常要盯住“这次请求到底发生了什么”,而不是扫全量日志。FrankenPHP 没有内置 trace_id,但你可以轻量注入:
- 在入口
public/index.php开头加:$request_id = bin2hex(random_bytes(6)); putenv("REQUEST_ID=$request_id"); - 所有
error_log()或trigger_error()前缀加[$request_id],例如:error_log("[$request_id] DB query: SELECT * FROM users", 0) - 终端里用
grep "\[$request_id\]"一键过滤(注意方括号要转义)
这个做法不依赖任何扩展,也不改日志格式,只是给每条人工打点加个锚点。比等 JSON 解析再查 context.request_id 快得多。
别忽略 stderr 和 stdout 的分流差异
FrankenPHP 把 PHP 错误(Parse Error、Fatal Error)打到 stderr,而 echo、print、error_log($msg, 0) 默认走 stdout。终端里它们颜色不同(通常 stderr 红,stdout 白),但重定向或管道时容易混掉:
- 前台运行时:
./frankenphp serve 2>&1 | grep -E "(ERROR|FATAL|Exception)"—— 这样能抓到真正的崩溃 - 用
docker logs -f时,容器引擎默认合并两者,但docker logs --details可看到stream字段标明是stdout还是stderr - 如果发现
var_dump()不显示,先检查是不是被ob_start()拦截了,而不是怀疑日志配置
最常被忽略的是:你在 IDE 终端里看到的“空白”,可能只是 stdout 被缓冲了(尤其用了 fastcgi_finish_request() 后)。加 flush(); ob_flush(); 或直接用 error_log()(它默认不缓冲)就能立刻看到。



















