PHP 日志实战:从 error_log 到 PSR-3 结构化日志
PHP 日志中文实战:error_log() 与 php.ini 配置(error_reporting/log_errors/display_errors)、PHP 错误级别体系、syslog 输出、异常与 set_exception_handler、PSR-3 标准与 Monolog 简介,以及观测云 DataKit 采集落地方案。
PHP 内置的错误记录机制(error_log() + php.ini 指令)能覆盖基础需求,而 PSR-3 标准和 Monolog 则把 PHP 日志带入了结构化时代。本文从内置机制讲到现代实践,帮助你为 PHP 应用搭好日志地基。
核心要点速览
- php.ini 三件套:生产环境
display_errors=Off、log_errors=On、error_log指向固定文件——错误不外泄到页面、全部落盘。 - error_log() 手动记录:第二个参数控制去向(0 系统日志/文件、3 追加到指定文件、4 输出到 stderr)。
- PHP 错误级别:E_ERROR、E_WARNING、E_NOTICE、E_DEPRECATED 等,
error_reporting控制报告范围。 - 现代 PHP 日志 = PSR-3 + Monolog:业务代码面向 PSR-3 接口,Monolog 提供实现(见专篇)。
php.ini:PHP 错误处理的总开关
; 生产环境标准配置
display_errors = Off ; 错误不输出到页面(防信息泄漏)
log_errors = On ; 错误写入日志
error_log = /var/log/php/error.log
error_reporting = E_ALL & ~E_DEPRECATED & ~E_NOTICE
开发环境可以把 display_errors 打开、把 error_reporting 设为 E_ALL 尽收所有提示。生产环境的铁律:错误永远不进浏览器——页面上的堆栈是攻击者的情报来源。
error_log():手动写日志
// 写入 php.ini 配置的默认位置
error_log("订单创建失败 order_id=1024");
// 追加到指定文件
error_log("支付回调: " . json_encode($payload), 3, "/var/log/php/payments.log");
// 输出到 stderr(容器环境推荐,docker logs 可见)
error_log("队列消费异常", 4);
简单场景够用,但它没有级别概念、没有结构化字段——正式应用应该用 PSR-3 日志库。
PHP 错误级别体系
| 级别常量 | 含义 |
|---|---|
| E_ERROR | 致命运行时错误,脚本终止 |
| E_WARNING | 运行时警告,脚本继续 |
| E_PARSE | 解析错误 |
| E_NOTICE | 可能的 Bug 提示 |
| E_DEPRECATED | 弃用特性警告 |
| E_CORE_ERROR / E_COMPILE_ERROR | PHP 核心/编译期致命错误 |
error_reporting(E_ALL & ~E_DEPRECATED) 这类位运算表达式控制哪些级别会被报告。框架(Laravel/Symfony)会把 PHP 错误转换成异常统一处理。
异常与未捕获异常兜底
try {
$payment->charge($order);
} catch (Throwable $e) {
error_log("扣款异常: " . $e->getMessage() . "\n" . $e->getTraceAsString());
throw $e;
}
全局兜底(程序级未捕获异常):
set_exception_handler(function (Throwable $e) {
error_log("未捕获异常: {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}");
});
异常日志必须带堆栈(getTraceAsString()),否则只剩一行消息无从排查。
syslog 输出
openlog("myapp", LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog(LOG_WARNING, "配置缺失,使用默认值");
closelog();
走 syslog 后日志进入系统日志体系(rsyslog/journald),可与系统日志统一收集。LOG_LOCAL0 等 facility 便于在 rsyslog 规则中按应用分流。
PSR-3:PHP 日志的标准接口
PSR-3 定义了 Psr\Log\LoggerInterface:八个级别方法(emergency/alert/critical/error/warning/notice/info/debug)+ context 数组传结构化字段:
$logger->info("用户购买", ["item" => "Orange Soda", "price" => 100]);
$logger->error("支付失败", ["order_id" => 1024, "exception" => $e]);
业务代码面向 PSR-3 接口编程,实现用 Monolog(PHP 事实标准,Laravel/Symfony 默认)——这就是现代 PHP 日志的标准姿势。详见本系列 Monolog 专篇。
观测云落地:PHP 日志采集与分析
- 应用侧:框架日志(Laravel/Symfony 走 Monolog)JSON 输出到 stdout(容器)或
storage/logs文件(主机);PHP-FPM 自身的错误日志(error_log 指向)一并纳入采集范围。 - DataKit 采集:容器 stdout 自动采集;主机在
conf.d/log/logging.conf配置logfiles指向 PHP 错误日志与应用日志文件,source: php-app、service标识服务。 - Pipeline 标准化:JSON 自动解析;纯文本 PHP 错误日志用 Pipeline grok 提取级别与消息,映射标准
status(ERROR/FATAL →error)、time。 - 检索告警:日志查看器按 status/service 过滤,聚类分析归纳异常;监控器对 error 日志与 Fatal error 关键字设阈值告警,通知对象发钉钉/企业微信/飞书。
- 链路关联:接入 OpenTelemetry PHP 探针后 trace_id 注入日志,与观测云 APM 双向跳转。
- 成本控制:多索引按级别拆分保留,历史数据转发归档对象存储。
常见问题(FAQ)
生产环境 display_errors 一定要关吗?
一定。开启会把堆栈、路径、甚至配置片段暴露给终端用户——安全与体验双重事故。错误详情只进日志,页面只显示友好提示。
PHP-FPM 的 error_log 和应用的 Monolog 日志是两回事吗?
是两套:PHP-FPM/PHP 引擎自己的错误(如 fatal、超时)走 php.ini 的 error_log;应用主动记录的日志走 Monolog handler。两者都要采集——前者管"引擎病了",后者管"业务病了"。
error_log() 和 Monolog 能混用吗?
技术上行,但不建议。error_log 没有级别和结构化字段,混用会让平台侧解析规则翻倍。统一用 PSR-3 接口,老代码逐步迁移。
CLI 脚本(定时任务)的日志怎么处理?
CLI 下同样生效:error_log(4) 输出 stderr,crontab 里重定向到文件或交给日志采集。更简单的是脚本内统一用 Monolog 写 stdout,由 DataKit 采集,避免每台机器散落日志文件。
系列阅读
- 上一篇:Java 日志库对比
- 下一篇:Monolog 实战指南
- 相关阅读:Laravel 日志实战 | 日志级别详解