生产环境日志应分层精简:caddy用json格式访问日志并禁用纯文本;php错误日志须落盘至固定路径且禁用syslog/stderr;业务日志必须用monolog+streamhandler;所有日志文件需由logrotate配合copytruncate管理。

生产环境不该“保留所有日志”,而应只保留可定位问题、可审计行为、且不拖垮磁盘或性能的日志。FrankenPHP 的日志来源分散,必须分层取舍:Caddy 层、PHP 运行时层、应用业务层,各自保留策略完全不同。
Caddy 访问日志只留结构化 JSON,禁用纯文本
Caddy 默认的纯文本访问日志(log { output file /var/log/frankenphp/access.log })在高流量下极易撑爆磁盘、难解析、无上下文。生产中应强制走 JSON 格式:
-
log块中必须加format json,确保每行是合法 JSON,字段含status、method、uri、duration、remote_ip - 绝对不要用
format single_field common或默认格式——它无法被 Filebeat/Elasticsearch 正确提取字段 - 若日志量极大(如 >1000 QPS),可考虑关闭
log块,改用 Caddy 的handle+reverse_proxy日志透传到上游,把日志采集压力交给更专业的边缘代理
PHP 错误日志必须落盘,但只保留 error_log 配置路径
FrankenPHP 不接管 PHP 的错误日志路径,完全依赖 php.ini 中的 error_log 和 log_errors = On。生产中要确保:
-
error_log指向一个固定、有写权限的绝对路径,例如/var/log/php/error.log,不能是syslog或stderr(后者在容器里会被 docker logs 缓存,删容器即丢) -
error_reporting设为E_ALL & ~E_DEPRECATED & ~E_STRICT,避免噪音干扰真实错误 - 禁止在代码里调用
error_log($msg, 3, $file)手动写文件——它绕过php.ini配置,权限/并发/轮转全不可控
应用业务日志必须走 Monolog,且禁用 file_put_contents
订单、登录、支付回调等业务动作,不能靠 file_put_contents() 或裸 error_log() 记录。原因很实际:
- 并发写入时会乱序、覆盖、丢行(PHP 文件 I/O 无原子锁)
- 没轮转逻辑,
app.log几天就涨到 GB 级 - 客户环境无法保证
/tmp或相对路径存在 - 正确做法是用
Monolog\Handler\StreamHandler写入/var/log/myapp/app.log,并确保该目录由部署脚本提前创建、属主设为运行用户(如www-data)
别碰 FrankenPHP 主进程 stdout/stderr 做持久化
很多人想用 docker logs 或 journalctl -u frankenphp 当主力日志源,这是错的:
-
docker logs底层读的是/var/lib/docker/containers/xxx/xxx-json.log,容器rm后彻底消失 -
journalctl默认只保留最近 100MB 或 2 周(取决于SystemMaxUse),不是归档方案 - 这些输出混杂了启动信息、TLS 握手、worker 启停等低价值内容,真正要查的 PHP Fatal 或 500 错误,早被刷屏冲走
- 唯一合理用途是快速排障:
docker logs --since 5m my-app | grep -i "fatal\|500",查完就丢
真正关键的点在于:FrankenPHP 自身不轮转、不压缩、不归档任何日志。你必须用 logrotate 管理所有落盘文件(/var/log/php/*.log、/var/log/myapp/*.log),且每个配置里必须带 copytruncate ——否则 Caddy 或 PHP 进程会因文件句柄未刷新而继续往旧 inode 写,导致磁盘不释放。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











