frankenphp容器日志默认不持久化,需挂载/var/log/php和/var/log/caddy到宿主机,或配置caddy输出stdout并用filebeat采集;php应用应避免error_log(...,3)写文件,统一走stdout/stderr。

FrankenPHP 容器日志默认不持久化,docker logs 查到的内容只是容器运行时的 stdout/stderr 缓存,容器删除后就彻底丢失。要真正持久保存,必须把日志路径显式挂载到宿主机或外部存储。
为什么 docker logs 不等于持久化
很多人误以为只要日志输出到 stdout 就“自动持久”,其实不然:docker logs 读的是容器的 json.log 文件(位于 /var/lib/docker/containers/<id>/<id>-json.log</id></id>),这个文件本身受 Docker daemon 管理,且默认不压缩、不轮转、不清理——一旦容器被 rm 或重建,该文件就被删掉。它不是备份,只是临时缓存。
挂载 /var/log/php 和 /var/log/caddy 到宿主机
FrankenPHP 中 PHP 错误日志和 Caddy 访问日志默认写入文件系统,路径分别是:
-
/var/log/php/error.log(PHPerror_log()默认目标) -
/var/log/caddy/access.log和/var/log/caddy/error.log(Caddy 原生日志)
启动容器时用 -v 显式挂载:
docker run -v $(pwd)/logs/php:/var/log/php \
-v $(pwd)/logs/caddy:/var/log/caddy \
dunglas/frankenphp
这样即使容器重启或重建,日志文件仍保留在宿主机 ./logs/ 下。注意:需提前创建目录并确保容器内 UID 可写(FrankenPHP 容器以 UID 1001 运行)。
让 Caddy 日志走 stdout + Filebeat 持久采集
如果更倾向结构化日志(JSON 格式),推荐关闭 Caddy 文件日志,改用 stdout 输出,再由 Filebeat 或 Fluent Bit 持久转发:
- 加环境变量
-e CADDY_LOG_OUTPUT=stdout(仅 FrankenPHP 0.19+ 有效) - PHP 应用层统一用
error_log(json_encode($log), 4)写 stderr(4表示 stderr) - Filebeat 配置中设置
paths: ["/var/log/containers/*.log"](K8s)或直接监听容器 stdout(Docker) - 关键:启用
json.keys_under_root: true,否则字段不会提升到顶层
这种方式下,“持久化”实际由日志后端(如 Elasticsearch、Loki、S3)承担,容器本地不留日志文件,避免磁盘撑爆。
PHP 应用日志别依赖 error_log($msg, 3, $file)
手动指定文件路径写日志(如 error_log("xxx", 3, "/app/storage/logs/app.log"))看似简单,但容易踩坑:
- 路径不在挂载卷内 → 容器删除即丢失
- 多线程并发写同一文件 → 日志错乱、截断(FrankenPHP 是多线程模型)
- 没配 logrotate → 单文件无限增长,最终触发 OOM 或磁盘满
正确做法是:所有应用日志统一走 error_log(..., 0)(stdout)或 error_log(..., 4)(stderr),交由外部采集器处理;若必须落盘,确保该路径已挂载且有轮转策略(如用 logrotate 容器 sidecar)。
最常被忽略的一点:Caddy 的 /var/log/caddy 和 PHP 的 /var/log/php 是两个独立路径,挂载时不能只挂一个;同时,CADDY_LOG_OUTPUT=stdout 和文件日志是互斥的——开了前者,后者就不再写文件,别两边都配还指望日志重复留存。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











