frankenphp_request_id() 是唯一可靠的请求 id 来源,它在每个 http 请求进入时自动生成唯一、短小、url 安全的字符串,跨协程和 php 生命周期稳定,且与 caddy 日志中 request_id 字段完全一致。

frankenphp_request_id() 是唯一可靠的请求 ID 来源
FrankenPHP 在每个 HTTP 请求进入时自动生成一个唯一、短小、URL 安全的字符串 ID,可通过 frankenphp_request_id() 函数获取。它不是 $_SERVER['REQUEST_ID'] 或自定义 bin2hex(random_bytes(8)),后者在 worker 复用下可能重复或丢失上下文。这个函数返回值已确保跨协程、跨 PHP 生命周期稳定,且与 Caddy 日志中的 request_id 字段完全一致。
想写进 access.log 必须用 JSON 格式 + log_fields
文本格式(common/combined)的 access log 不支持注入自定义字段,log_fields 只对 log_format: json 生效。配置要两步走:
- 在
frankenphp.yaml的http块下设log_format: json和access_log路径 - 在同一层级加
log_fields,引用$_SERVER中预设的键,例如:request_id:"$_SERVER[FRANKENPHP_REQUEST_ID]" - 入口 PHP(如
public/index.php)开头必须先写:$_SERVER['FRANKENPHP_REQUEST_ID'] = frankenphp_request_id();
漏掉任何一步,JSON 日志里都看不到 request_id 字段。
error_log() 记录请求 ID 要手动拼接,别依赖自动注入
error_log() 不会自动带上请求 ID,你得自己塞进去。最简方式是:
$reqId = frankenphp_request_id();
error_log("[$reqId] User registered: " . json_encode($_POST, JSON_UNESCAPED_UNICODE), 4);
注意几点:
- 不要用
ini_set('error_log', ...)试图全局绑定路径——它在 FrankenPHP 的 HTTP 模式下常被重置 - 别把
frankenphp_request_id()放在error_log()参数里直接调用(如error_log("[".frankenphp_request_id()."] ...")),PHP 会先求值再传参,但万一出错(比如超时中断),ID 就丢了 - 如果用 Monolog,需注册 Processor 显式注入
request_id到$record['extra'],否则它只记录时间、级别、消息
别在 Caddyfile 里用 {log} 块硬塞 request_id
Caddy v2 的 log 指令不识别 frankenphp_request_id(),也无法读取 PHP 层变量。你在 Caddyfile 里写 "request_id": "{http.request.id}" 这类模板,实际输出是空字符串或字面量 {http.request.id},不是真实 ID。FrankenPHP 的请求 ID 是 PHP SAPI 层生成的,Caddy 在日志渲染阶段根本拿不到——它只认自己解析请求时产生的 ID(和 FrankenPHP 不一致)。所以这条路走不通,必须回到 frankenphp.yaml + log_fields + PHP 预设 $_SERVER 的组合。
最容易被忽略的是:每次调用 frankenphp_request_id() 都必须发生在当前请求生命周期内,不能缓存到静态变量或全局数组里复用;worker 复用机制下,上一个请求的 ID 会污染下一个请求的日志。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











