PHP日志聚合关键在源头结构化、采集低损、告警分级:须用Monolog+JsonFormatter输出标准JSON,禁用LineFormatter;中小项目优选filebeat+Loki替代ELK;SQL日志须独立通道并脱敏;采样应按request_id哈希取模保障链路完整性。

PHP日志聚合不是“配个filebeat就能跑”,关键在日志源头是否结构化、采集链路是否低损、告警是否能区分噪音和真异常。小项目硬上ELK,运维成本反超业务收益;大流量场景用file_put_contents写日志,磁盘IO和锁竞争会直接拖垮QPS。
PHP日志必须先结构化,否则后续所有聚合都是徒劳
非结构化日志(如error_log("DB timeout at ".date('Y-m-d H:i:s')))无法被Logstash或Filebeat可靠解析,grok规则极易失效,字段提取失败率高。Monolog默认输出是文本行,需显式启用JSON格式:
- 用
Monolog\Handler\StreamHandler时,搭配Monolog\Formatter\JsonFormatter,确保每条日志是合法JSON对象 - 禁用
Monolog\Formatter\LineFormatter——它把堆栈跟踪压成单行,破坏可读性与解析稳定性 - 手动注入
request_id、service_name、env等字段,避免后期靠正则从message里硬抠 - 注意:Laravel默认
stack通道若未显式配置jsonformatter,日志仍是纯文本
filebeat + Loki 比 ELK 更适合中小PHP项目
ELK对PHP项目常属“杀鸡用牛刀”:Elasticsearch内存占用高,Logstash JVM启动慢,Kibana权限模型复杂。Loki+Promtail(或filebeat)轻量且原生适配标签路由:
-
filebeat配置中用fields打标:service: "user-api"、env: "prod",Loki按标签索引,查询快、存储省 - 不用
grok解析JSON——Loki本身不解析日志内容,只索引标签,真正解析交给前端Grafana的LogQL - PHP进程无需直连Loki,
filebeat作为边车(sidecar)运行,崩溃不影响主服务 - 避坑点:
filebeat默认close_inactive: 5m,高频日志文件可能被提前关闭,导致尾部丢失,建议调至1h或改用close_eof: true
错误告警必须分级,否则钉钉/企微群会沦为垃圾场
把所有WARNING都发告警,等于没告警。真实生产环境应分三级:
立即学习“PHP免费学习笔记(深入)”;
-
FATAL/UNCAUGHT_EXCEPTION:立即触发企业微信机器人+电话轮询(用
supervisor或systemd守护的告警脚本),这类错误必须人工介入 - ERROR(含SQLSTATE、cURL 0、Redis timeout):聚合5分钟内超10次才发消息,避免慢SQL抖动刷屏
- WARNING(如缓存穿透、降级开关开启):仅写入Loki,Grafana设看板阈值,不主动推送
- 关键细节:
register_shutdown_function捕获的致命错误,必须检查$error['type']是否为E_ERROR或E_PARSE,E_WARNING不能进告警流
PDO SQL日志必须走独立通道,别混进应用日志
把SQL日志和业务日志塞进同一个文件,会导致Loki/Grafana查询变慢、采样失真、告警误判。正确做法是分离通道:
- 用Monolog单独创建
sql_logger实例,handler指向/var/log/php-sql.log - 在PDO包装器中,
execute()耗时超过100ms时,才记录完整SQL+参数(敏感字段如password需脱敏) -
filebeat为SQL日志单独配一个input,fields.level: "sql_slow",便于Grafana按{level="sql_slow"}过滤 - 严禁在
__destruct()里记录SQL——对象销毁顺序不确定,PDO连接可能已关闭,getAttribute(PDO::ATTR_SERVER_VERSION)会报错
最易被忽略的是日志采样策略:全量日志在高并发下必然压垮存储和网络,但固定1%采样又可能漏掉关键路径。实际应按request_id哈希后取模,保证同一请求的所有日志要么全采、要么全丢,才能做完整链路分析。



















