frankenphp日志时间戳需分层识别:caddy层access.log的ts字段为浮点秒(如1728858045.123456),php层需用microtime(true)或new datetime()获取高精度时间,log_fields注入须预设变量,stderr混杂多源时间应以json结构和上下文为准。

FrankenPHP 日志里的时间戳不是直接“看”出来的,得先确认它来自哪一层、用的什么格式、有没有被覆盖或误读——否则你看到的 1728858000 或 "2024-10-12T08:30:45+00:00 很可能根本不是你想要的那个“请求发生时间”。
access.log 里的 timestamp 是 Caddy 层生成的,不是 PHP 的
FrankenPHP 的 access.log 实际由内嵌的 Caddy 提供,它的时间戳字段(比如 ts)默认是浮点数,单位是秒(含微秒),例如 "ts":1728858045.123456。这个时间是 Caddy 接收到请求那一刻打的,和 PHP 执行无关。
- 它不随
date_default_timezone_set()变,也不受 PHP 时区影响 - 如果配了
log_format: json,ts字段固定存在,但值不可改、不可注入 - 别用
date('c', (int)$ts)直接转——小数部分会丢,精度损失明显 - 正确做法是用
DateTime::createFromFormat('U.u', $ts)或new DateTime("@$ts")保留微秒
PHP 应用层打的日志时间戳必须自己生成
你在 error_log(json_encode([...]), 0) 里塞的 timestamp 字段,才是业务真正需要的时间点。但它不会自动产生,必须显式写入。
- 别用
time()—— 它返回整秒,丢失毫秒,且在 worker 协程中可能因调度延迟不准 - 推荐用
(new DateTime())->format('c')或date(DATE_ISO8601),确保带时区、ISO 格式、可被 Logstash 直接解析 - 如果要用 Unix 时间戳(比如存数据库),用
microtime(true)而不是time(),它返回带微秒的浮点数 - 注意:
$_SERVER['REQUEST_TIME_FLOAT']是 PHP 接收到请求时的高精度时间,比你在控制器里调new DateTime()更接近真实起点
frankenphp.yaml 里 log_fields 注入的时间字段容易踩坑
你可以在 log_fields 里写 request_time:"$_SERVER[REQUEST_TIME_FLOAT]",让 Caddy 层日志带上 PHP 的时间戳。但这只是字符串插值,不是执行函数。
-
REQUEST_TIME_FLOAT是浮点数,但插入后变成 JSON 字符串,比如"1728858045.123456",后续解析要小心类型转换 - 不能写
now:"date('c')"—— 这会被原样当字符串输出,不会执行date() - 如果想注入格式化后的时间,得提前在 PHP 入口设好:
$_SERVER['X_LOG_TS'] = (new DateTime())->format('c');,再在配置里引用log_ts:"$_SERVER[X_LOG_TS]" - 字段为空时(如
$_SERVER键不存在),JSON 中对应值为null,不是省略,采集端需兼容
stderr 里混着多种时间戳,得靠上下文区分
FrankenPHP 启动失败、panic、PHP 解析错误等都打在 stderr,里面的时间戳来源杂:Caddy 初始化日志用自己格式,PHP error_log() 输出依赖 php.ini 的 log_errors 和 error_log 设置,而你自己写的 error_log(json_encode(...)) 又是另一套。
- 不要假设所有行开头的
[2024-10-12 08:30:45]都是同一来源——Caddy 日志、PHP 错误日志、自定义 JSON 日志三者前缀格式可能重叠但语义不同 - 最稳的识别方式是:看整行是否是合法 JSON;是,则
timestamp字段可信;不是,则优先信ts(Caddy)或系统 stderr 前缀(如 Docker 默认时间) - worker 模式下,
date('c')在长协程中可能卡在旧时间,务必用new DateTime()实例化新对象
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











