stdout日志无输出因sapi差异:web下写入响应体被拦截,cli才输出终端;nginx+php-fpm时实际写入fpm error_log。

日志驱动从 File 换成 Stdout 为什么没输出?
ThinkPHP 6+ 默认的 File 驱动会静默写入文件,换成 Stdout 后却看不到日志,不是配置失效,而是 CLI 和 Web SAPI 的行为差异导致的。Web 环境下 Stdout 实际写到响应体里,容易被框架输出拦截或浏览器忽略;CLI 下才真正输出到终端。
- 只在命令行调试时用
Stdout,上线环境必须切回File或Socket -
Stdout不支持日志级别过滤,所有级别都会输出,容易刷屏 - 若用 Nginx + PHP-FPM,
Stdout写入的是 FPM 的 error log,需查error_log配置路径,不是浏览器控制台
think\log\driver\File 的 realtime_write 设为 true 有啥代价?
默认 realtime_write = false,TP 把日志暂存在内存,请求结束统一 flush;设为 true 则每条日志立刻 fwrite,能避免进程崩溃导致日志丢失,但会显著拖慢高并发场景下的响应速度。
- 单次请求日志量大(比如循环里打 100 条)时,I/O 次数从 1 次变成 100 次
- 多进程写同一日志文件时,
realtime_write = true可能引发内容错乱(TP 未做文件锁,依赖系统级 append 模式保障) - 云环境(如阿里云函数)中,临时磁盘 I/O 延迟高,开启后 RT 普遍增加 2–5ms
自定义日志处理器怎么接进 monolog 而不破坏 TP 日志生命周期?
TP 6.1+ 允许通过 logger.handler 配置项注入 Monolog 实例,但直接 new 一个 Monolog\Logger 会导致上下文丢失(比如请求 ID、用户 UID),因为 TP 的 Channel 和 Context 机制没透传过去。
- 必须继承
think\log\driver\Channel,重写save()方法,在里面调用 Monolog 的log(),同时把$context里的request_id、user_id打包进去 - 不要在
handler配置里写匿名函数,TP 不支持序列化闭包,部署时会报Unserialize failed - Monolog 的
RotatingFileHandler和 TP 的日期分片逻辑冲突,禁用 TP 的file_size和days,全交给 Monolog 控制
日志格式里想加 trace_id,但 traceid 字段总为空
TP 自带的 traceid 是按请求生成的,但默认只注入到 Log::record() 的上下文,而自定义格式器(formatter)拿到的是原始 $data 数组,没经过上下文合并。
- 必须在
app\common\log\Formatter类里重写format(),先调用parent::format()得到基础结构,再用think\Container::get('app')->getTraceID()补字段 - 如果用了 Swoole,
getTraceID()在协程间可能复用,得配合Co::getuid()做二次区分 - JSON 格式下字段名必须小写加下划线(
trace_id),否则 ELK 收集时无法自动映射
日志格式化和上下文传递是两层逻辑,TP 没把它们焊死在一起,改的时候得盯住数据流从 record → handler → formatter 的每个环节,漏一环就丢字段。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











