CentOS PHP-FPM多池日志分离,error_log按站点切割配置
线上跑着十几个站点的服务器,一到排障就头大:所有PHP报错全挤在/var/log/php-fpm/error.log里,grep半天也揪不出是哪台虚拟主机在闹脾气。把日志按站点切开,既能缩短定位时间,又能避免单个刷屏站点把磁盘打满。下面这套办法,在CentOS Stream 8、AlmaLinux 9实测通过,php-fpm版本8.1,同样适用于7.4,老掉牙的5.4也能照抄,只要注意路径差别。

为什么要做多池日志分离
默认的php-fpm.conf只启了一个[www]池,所有站点的请求混在一个池里,错误自然写进同一个文件。想分账,就得给每个站点单独开池,再让各池把日志写到自己的目录。好处立竿见影:
1. 排障时直接tail对应站点的error.log,再也不用awk过滤域名。
日志轮转可以按站点维度做,不同业务保留周期不同,不怕“一刀切”。
某个池爆内存或IO,只影响它自己,别的站点稳如老狗。
配合ELK/Graylog采集,字段里自带pool名称,检索速度翻倍。

前置准备:目录与权限
先给日志安个家,习惯放在/home/logs下,磁盘大就挂单独分区:
mkdir -p /home/logs/{siteA,siteB,siteC}/php
chown -R php-fpm:php-fpm /home/logs
chmod 750 /home/logs/*
SELinux开着就别忘打上下文:

semanage fcontext -a -t httpdlogt "/home/logs(/.*)?"restorecon -Rv /home/logs
拆池:给每个站点单独写conf
php-fpm支持通配包含,把单个池文件丢进/etc/php-fpm.d/即可。以siteA为例,新建/etc/php-fpm.d/siteA.conf:
[siteA]user = siteA
group = siteA
listen = /run/php-fpm/siteA.sock
listen.owner = nginx
listen.group = nginx
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.minspareservers = 5
pm.maxspareservers = 15
;关键行:错误日志独立
tabphpadminvalue[error_log] = /home/logs/siteA/php/error.log
tabphpadminflag[log_errors] = on
tabphpadminflag[display_errors] = off
;慢日志也顺手切
tabphpadminvalue[slowlog] = /home/logs/siteA/php/slow.log
tabrequestslowlogtimeout = 3s
siteB、siteC照抄,改池名和路径即可。reload php-fpm后,ps aux | grep php-fpm能看到三个pool已经各跑各的。
Nginx虚拟主机对接不同sock
server块里把fastcgi_pass指到对应socket,日志自然就分开:
server {
server_name siteA.example.com;
root /home/siteA/web;
access_log /home/logs/siteA/nginx/access.log main;
error_log /home/logs/siteA/nginx/error.log warn;
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/siteA.sock;
include fastcgi_params;
}
}
reload nginx,访问故意写错的代码,立即能在/home/logs/siteA/php/error.log里看到Fatal error,别的池纹丝不动。
轮转策略:logrotate按站点配置
新建/etc/logrotate.d/php-fpm-sites:
/home/logs//php/.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
sharedscripts
postrotate
/usr/bin/systemctl reload php-fpm > /dev/null 2>&1 || true
endscript
}
用通配符一次管所有子目录,reload信号让php-fpm重新打开文件句柄,零丢失。
踩坑汇总
1. 权限拒绝:error_log路径必须让php-fpm用户可写,SELinux没打标签也会报Permission denied。
2. 值覆盖顺序:phpadminvalue优先级高于php.ini,如果之前把error_log设成syslog,这里不改成文件就永远写不进去。
3. 磁盘打爆:某个站点若疯狂报错,单文件也能涨到几十G,建议加inotify脚本实时告警,或者直接上lograte的maxsize 100M。
4. 池名重复:两段配置里如果都写[www],后加载的会覆盖前者,排障时一脸懵。
5. 日志丢失:reload和restart区别很大,改完配置直接restart会中断当前请求,用reload更平滑。
一键检查脚本
把下面内容存成checkfpmlog.sh,跑完就能知道哪些池没写日志:
#!/bin/bashfor pool in /etc/php-fpm.d/*.conf; do
name=$(awk -F'[][]' '/^\[.*\]$/{print $2}' "$pool")
err=$(awk -F'= *' '$1=="phpadminvalue[error_log]"{print $2}' "$pool")
if [[ -n "$err" ]]; then
printf "%-15s error_log=%s\n" "$name" "$err"
tail -n1 "$err" 2>/dev/null || echo " file not writable"
else
echo "$name missing error_log"
fi
done
多池+独立error_log,其实就是“分家”思路:让各自站点自己管自己,排障不用翻山越岭,磁盘告警也能精准到人。配置一次,省心一年,下次再遇到白屏,直接tail对应站点的日志,三十秒就能定位是哪行代码在作妖。
