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 到 PSR-3 结构化日志技术指南封面

PHP 内置的错误记录机制(error_log() + php.ini 指令)能覆盖基础需求,而 PSR-3 标准和 Monolog 则把 PHP 日志带入了结构化时代。本文从内置机制讲到现代实践,帮助你为 PHP 应用搭好日志地基。

核心要点速览

  • php.ini 三件套:生产环境 display_errors=Offlog_errors=Onerror_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 日志采集与分析

  1. 应用侧:框架日志(Laravel/Symfony 走 Monolog)JSON 输出到 stdout(容器)或 storage/logs 文件(主机);PHP-FPM 自身的错误日志(error_log 指向)一并纳入采集范围。
  2. DataKit 采集:容器 stdout 自动采集;主机在 conf.d/log/logging.conf 配置 logfiles 指向 PHP 错误日志与应用日志文件,source: php-appservice 标识服务。
  3. Pipeline 标准化:JSON 自动解析;纯文本 PHP 错误日志用 Pipeline grok 提取级别与消息,映射标准 status(ERROR/FATAL → error)、time
  4. 检索告警:日志查看器按 status/service 过滤,聚类分析归纳异常;监控器对 error 日志与 Fatal error 关键字设阈值告警,通知对象发钉钉/企业微信/飞书。
  5. 链路关联:接入 OpenTelemetry PHP 探针后 trace_id 注入日志,与观测云 APM 双向跳转。
  6. 成本控制:多索引按级别拆分保留,历史数据转发归档对象存储。

常见问题(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 采集,避免每台机器散落日志文件。

系列阅读


获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台