最稳妥的PHP日志写入方式是fopen($file, 'a')配合fwrite,确保追加且不覆盖;需检查返回值、及时fclose;error_log和file_put_contents各有坑点;生产环境应优先使用Monolog等标准库。

PHP用fopen追加写入日志最稳妥
直接用fopen($file, 'a')打开文件,再用fwrite写入,是PHP写日志最基础也最可控的方式。'a'模式保证文件指针始终在末尾,即使并发写入也不会覆盖已有内容(但需注意系统级文件锁问题)。
常见错误是误用'w'模式——它会清空整个文件;或者用'a+'却没调用fseek就读,导致读不到新写入的内容。
-
'a':只写,自动定位到末尾,文件不存在则创建 -
'a+':可读可写,但读操作默认从开头开始,需手动fseek($fp, 0, SEEK_END)才能读到最新行(一般日志不推荐读) - 务必检查
fopen返回值是否为false,避免静默失败 - 写完记得
fclose($fp),否则可能缓冲未刷盘或句柄泄漏
用error_log函数写日志要注意路径和权限
error_log可以绕过自己管理文件句柄,适合快速记录简单信息,但它默认写入error_log配置的路径(通常是Web服务器错误日志),不是你想写的那个app.log。
要指定文件,必须传第三个参数message_type = 3,并确保目标目录Web进程有写权限:
error_log(date('Y-m-d H:i:s') . " [INFO] User login\n", 3, '/var/log/myapp/app.log');
容易踩的坑:
- 路径不存在时不会自动创建父目录,得提前
mkdir -p /var/log/myapp - 如果
open_basedir限制了路径,会报Warning: error_log(): open_basedir restriction in effect - 写入内容末尾不自动换行,得自己加
\n
多进程/多线程下用file_put_contents要加FILE_APPEND
用file_put_contents($file, $msg, FILE_APPEND | LOCK_EX)比手写fopen更简洁,LOCK_EX能防止并发写入错乱(如两行日志挤在同一行)。
但注意:LOCK_EX是咨询性锁,依赖所有写入方都主动加锁才有效;且Windows下对NFS挂载点可能失效。
- 不要省略
FILE_APPEND——不加它会覆盖整个文件 -
LOCK_EX会阻塞,高并发场景下可能拖慢响应,可考虑用队列或异步写入 - 如果日志量大,频繁调用
file_put_contents比复用fopen句柄开销更大
生产环境别手写日志逻辑,优先用Psr\Log\LoggerInterface
直接拼接字符串、硬编码路径、忽略时区或格式统一性,上线后很快变成维护黑洞。Composer安装monolog/monolog,几行就能获得轮转、级别过滤、上下文注入等能力:
$logger = new Logger('app');
$logger->pushHandler(new StreamHandler('/var/log/myapp/app.log', Logger::INFO));
$logger->info('User {user_id} logged in', ['user_id' => 123]);
关键提醒:
- Monolog默认不格式化时间戳为本地时区,
new DateTimeZone('Asia/Shanghai')得手动传给LineFormatter - StreamHandler的
$filename参数不支持~或$_SERVER['DOCUMENT_ROOT']等动态路径,得先解析成绝对路径 - 如果用
RotatingFileHandler,注意maxFiles设太小会导致旧日志被删,设太大又占磁盘
自研日志逻辑最容易被忽略的是错误抑制——比如fopen失败后继续fwrite,结果日志全丢,连“为什么没日志”都查不到。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











