Monolog 实战指南:PHP 日志的事实标准

Monolog 中文实战指南:Logger 频道、Handler/Processor/Formatter 三层架构、StreamHandler 与 RotatingFileHandler 轮转、八个日志级别、context 结构化字段、JsonFormatter JSON 输出、异常与全局异常处理,以及观测云 DataKit 采集落地方案。

最佳实践
Monolog 实战指南:PHP 日志的事实标准技术指南封面

Monolog 是 PHP 日志的事实标准——Laravel、Symfony 等主流框架的默认日志组件。它以 Handler(去向)、Processor(上下文)、Formatter(格式)三层架构提供了完整的日志能力。本文覆盖 Monolog 的核心用法与生产实践,并给出观测云平台接入方案。

核心要点速览

  • Logger 按频道(channel)组织new Logger("payments"),不同子系统独立频道、独立路由。
  • 三层架构:Handler 决定写到哪、Processor 附加公共字段、Formatter 控制输出格式。
  • RotatingFileHandler 内建轮转:按天切分、自动清理历史文件,无需 logrotate。
  • JsonFormatter 一行切换结构化:JSON 输出后观测云 DataKit 自动解析为字段。

快速上手:Logger 与 StreamHandler

composer require monolog/monolog
<?php
require __DIR__ . "/vendor/autoload.php";

use Monolog\Level;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$logger = new Logger("order-service");
$logger->pushHandler(new StreamHandler(__DIR__ . "/logs/app.log", Level::Info));

$logger->debug("调试细节");        // 不输出(Handler 级别 Info)
$logger->info("订单创建", ["order_id" => 1024]);
$logger->warning("库存不足", ["sku" => "A-01"]);
$logger->error("扣款失败", ["order_id" => 1024, "reason" => "余额不足"]);

输出(默认 LineFormatter):

[2026-08-25T14:55:22.123456+08:00] order-service.INFO: 订单创建 {"order_id":1024} []

第二个参数就是 context 数组——结构化字段的载体,会 JSON 序列化进日志。Monolog 遵循 PSR-3 的八个级别:debug/info/notice/warning/error/critical/alert/emergency。

频道(Logger 名)的价值paymentssecurityapi 各自建 Logger、挂不同 Handler,实现按子系统分流与独立级别控制。

Handler:日志去哪里

常用 Handler:

Handler 用途
StreamHandler 文件、php://stdout、任意流
RotatingFileHandler 按天轮转文件
SyslogHandler / SyslogUdpHandler 系统日志
FilterHandler 只放行指定级别的日志
FingersCrossedHandler 缓冲日志,出错时一次性写出(降噪神器)
use Monolog\Handler\RotatingFileHandler;

// 按天轮转,保留 30 天
$logger->pushHandler(new RotatingFileHandler(__DIR__ . "/logs/app.log", 30, Level::Info));

// 错误单独存档
$logger->pushHandler(new StreamHandler(__DIR__ . "/logs/error.log", Level::Error));

多 Handler 按 push 逆序匹配——更严格的级别限制放在后 push 的位置(栈顶先判断)。容器化部署直接 new StreamHandler("php://stdout")

Formatter:控制输出格式

use Monolog\Formatter\JsonFormatter;

$handler = new StreamHandler("php://stdout", Level::Info);
$handler->setFormatter(new JsonFormatter());
$logger->pushHandler($handler);

输出每行一个 JSON 对象(message、context、level、channel、datetime)——生产环境标准姿势,采集侧零解析成本。

Processor:自动附加公共字段

$logger->pushProcessor(function ($record) {
    $record->extra["hostname"] = gethostname();
    $record->extra["request_id"] = $_SERVER["HTTP_X_REQUEST_ID"] ?? null;
    return $record;
});

Monolog 内置了 WebProcessor(请求信息)、MemoryUsageProcessor、UidProcessor 等——公共字段一次配置,全频道生效。

异常与全局兜底

try {
    $payment->charge($order);
} catch (Throwable $e) {
    $logger->error("扣款异常", ["order_id" => $order->id, "exception" => $e]);
    throw $e;
}

// 全局兜底:未捕获异常一律记录
set_exception_handler(function (Throwable $e) use ($logger) {
    $logger->critical("未捕获异常", ["exception" => $e]);
});

"exception" => $e 是约定写法:JsonFormatter 会把异常的消息、类、堆栈完整序列化进 context。

观测云落地:Monolog 日志采集与分析

  1. 应用侧:JsonFormatter + php://stdout(容器)或 RotatingFileHandler(主机)。不建议在应用内用 Socket/HTTP Handler 直推日志后端——网络故障会拖累 PHP-FPM 进程,采集交给 DataKit。
  2. DataKit 采集:容器 stdout 自动采集;主机 conf.d/log/logging.conf 配置 logfiles 指向日志目录,source: php-appservice 标识服务。JSON 自动解析。
  3. Pipeline 标准化:Monolog 的 level_name(ERROR 等)映射标准 statusdatetime 解析为 time;context 字段按需提取顶层化。
  4. 检索告警:日志查看器按 status/channel/service 过滤,聚类分析归纳异常;监控器对 error 及以上级别设阈值,告警策略路由钉钉/企业微信/飞书。
  5. 链路关联:Processor 注入 trace_id(OpenTelemetry PHP),与观测云 APM 双向跳转。
  6. 成本控制:多索引按级别拆保留策略,历史数据转发归档对象存储。

常见问题(FAQ)

Monolog 频道和 Handler 级别怎么配合?

频道是逻辑分组(payments/security),每个频道挂自己的 Handler 栈;Handler 上的 Level 是最低放行级别。想"error 单独一个文件",就给该频道多 push 一个 Level::Error 的 StreamHandler。

FingersCrossedHandler 是什么原理?

它把日志先缓存在内存,一旦触发指定级别(如 Error)就把缓冲的全部上下文一次性写出——平时安静、出事时有完整现场,适合高 QPS 接口的错误排查。注意崩溃时缓冲内容会丢,需与常驻文件 Handler 搭配。

日志写入会影响 PHP-FPM 性能吗?

同步写文件通常很快,但网络类 Handler(TCP/HTTP)会阻塞 worker 进程,高并发下可能打满 FPM 进程池。生产只写本地 stdout/文件,传输交给 DataKit。

CLI 长进程(消费者)退出时日志会丢吗?

Monolog 默认同步写入,无缓冲丢失问题;若用了缓冲类 Handler,注册 pcntl_signal 在 SIGINT/SIGTERM 时优雅退出即可。

系列阅读


获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

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

立即开始

选择观测云版本

代码托管平台