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=Off、log_errors=On、error_log 指向固定文件——错误不输出到页面;能否落盘还需验证报告范围、路径和写入权限。
  • error_log() 手动记录:第二个参数控制去向(0 系统日志/文件、3 追加到指定文件、4 交给 SAPI 日志处理器)。
  • 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

开发环境可以把 display_errors 打开、把 error_reporting 设为 E_ALL 尽收所有提示。生产环境的铁律:错误永远不进浏览器——页面上的堆栈是攻击者的情报来源。

error_log():手动写日志

// 写入 php.ini 配置的默认位置
error_log("订单创建失败 order_id=1024");

// 追加到指定文件
error_log("支付回调: " . json_encode(["order_id" => $payload["order_id"] ?? null]) . PHP_EOL, 3, "/var/log/php/payments.log");

// 交给 SAPI 处理器;最终去向取决于 CLI/FPM 等运行方式
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) 这类位运算表达式控制哪些级别会被报告。框架可通过错误处理器把部分 PHP 错误转换成异常,范围取决于错误类型和配置。

异常与未捕获异常兜底

try {
    $payment->charge($order);
} catch (Throwable $e) {
    error_log("扣款异常: " . $e->getMessage() . "\n" . $e->getTraceAsString());
    throw $e;
}

CLI 程序的全局兜底(Web 请求应由框架统一处理状态码和响应):

set_exception_handler(function (Throwable $e) {
    error_log((string) $e);
    exit(1);
});

异常日志必须带堆栈(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 定义接口而不规定 JSON 输出格式。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-FPM 错误

先确认 Monolog 的输出文件和 PHP-FPM 的 error_log 位置。使用观测云时,在 DataKit 日志采集中为两类来源分别配置路径与解析规则;用一条应用异常和一条受控的 PHP 错误检查时间、级别与堆栈。

查询时先区分应用来源与 FPM 来源,再按已解析字段建立日志检测规则。只有采集到的错误记录才能参与检测,日志路径、权限或轮转变化后应重新验证。

常见问题(FAQ)

生产环境 display_errors 一定要关吗?

一定。开启会把堆栈、路径、甚至配置片段暴露给终端用户——安全与体验双重事故。错误详情只进日志,页面只显示友好提示。

PHP-FPM 的 error_log 和应用的 Monolog 日志是两回事吗?

应分开看:PHP 引擎错误由 php.ini 或 FPM pool 的覆盖设置决定;FPM 主进程有自己的 error_log;worker 的 stdout/stderr 是否进入 FPM 日志还取决于 catch_workers_output。应用主动日志由 Monolog handler 决定,应分别核对路径。

error_log() 和 Monolog 能混用吗?

技术上行,但不建议。error_log 没有级别和结构化字段,混用会让平台侧解析规则翻倍。统一用 PSR-3 接口,老代码逐步迁移。

CLI 脚本(定时任务)的日志怎么处理?

CLI 下同样生效:CLI 的 SAPI 日志通常写 stderr;需要明确写 stderr 可使用 fwrite(STDERR, $message . PHP_EOL),crontab 里重定向到文件或交给日志采集。更简单的是脚本内统一用 Monolog 写 stdout,由 DataKit 采集,避免每台机器散落日志文件。

系列阅读


[1]: 观测云文档《DataKit 日志采集综述》https://docs.guance.com/integrations/datakit-logging/
[2]: 观测云文档《日志查看器》https://docs.guance.com/logs/explorer/
[3]: 观测云文档《监控》https://docs.guance.com/monitoring/

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

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

立即开始

选择观测云版本

代码托管平台