在高性能 REST API 架构中,日志系统是监控、调试和运维的核心组件。Yii2 提供了强大且灵活的日志组件,但很多项目往往因配置不当导致日志混乱或性能下降。本文档定义了使用 PHP-FPM + Yii2 框架开发高性能 REST API 时的日志处理标准和最佳实践。
在微服务或前后端分离架构中,全链路追踪(Traceability)至关重要。核心思路是生成一个唯一的 Request ID,并让它贯穿整个请求生命周期(Request -> Logs -> Response -> Client)。
file_put_contents 或 fopen 手动写日志。Yii::info(), Yii::warning(), Yii::error() 记录。category 参数对日志进行精准分类,便于后续过滤和分发。X-Request-Id)Yii::info() 时不需要手动传 ID。/**
* 日志级别常量定义
*/
class LogLevel
{
// 系统级日志
const EMERGENCY = 'emergency'; // 系统不可用,需要立即处理
const ALERT = 'alert'; // 需要立即处理的错误
const CRITICAL = 'critical'; // 严重错误
// 应用级日志
const ERROR = 'error'; // 运行时错误
const WARNING = 'warning'; // 警告信息
const NOTICE = 'notice'; // 正常但重要的信息
// 调试级日志
const INFO = 'info'; // 一般信息
const DEBUG = 'debug'; // 调试信息(仅开发环境)
}
| 级别 | 触发场景 | 示例 |
|---|---|---|
| EMERGENCY | 数据库连接完全失败、服务完全不可用 | Redis 集群全部宕机 |
| ALERT | 第三方服务超时、配置错误 | 支付网关无响应 |
| CRITICAL | 关键业务异常、数据损坏 | 订单创建失败 |
| ERROR | 普通业务错误、可恢复异常 | 参数验证失败 |
| WARNING | 性能警告、潜在问题 | 响应时间超过阈值 |
| NOTICE | 重要业务事件 | 用户登录成功 |
| INFO | 一般业务事件 | API 请求统计 |
| DEBUG | 详细调试信息 | SQL 执行语句 |
{
"timestamp": "2024-01-15T10:30:00.000Z",
"level": "INFO",
"message": "API request completed",
"service": {
"name": "user-api",
"version": "2.1.0",
"environment": "production"
},
"request": {
"id": "req_abc123",
"method": "GET",
"path": "/api/v1/users/123",
"ip": "192.168.1.100",
"user_agent": "Mozilla/5.0",
"user_id": 456
},
"response": {
"status_code": 200,
"time_ms": 45.23
},
"context": {
"controller": "UserController",
"action": "view",
"additional_data": {}
},
"trace": {
"parent_span_id": "span_xyz789",
"span_id": "span_abc123"
}
}
不要把所有日志都塞进 app.log。应根据业务模块(如支付、用户中心、API对接)配置独立的 Log Target。
Yii2 默认的日志行为是按文件大小切分(轮转)。要实现“按天切分”并“长期保留”,需要对 logFile 进行动态配置。
'components' => [
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'flushInterval' => 1000, // 默认 1000,内存中积攒 1000 条内存才刷盘(重要性能参数)
'targets' => [
// 1. 全局错误日志 (兜底)
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
// 核心:文件名包含日期
'logFile' => '@runtime/logs/app-error' . date('Y-m-d') . '.log',
'maxFileSize' => 10240, // 10MB 切割
'maxLogFiles' => 1000, // 保留最近 20 个文件
'bgParams' => true, // 只有在通过 yiisoft/yii2-queue 等异步上下文中才需要考虑禁用
],
// 2. 核心业务日志 - 支付模块
[
'class' => 'yii\log\FileTarget',
'categories' => ['payment'], // 关键:只捕获 payment 分类的日志
'levels' => ['info', 'error', 'warning'],
'logFile' => '@runtime/logs/payment' . date('Y-m-d') . '.log',
'logVars' => [], // 生产环境建议清空,避免记录 GET/POST/COOKIE 等敏感冗余信息
'maxFileSize' => 10240, // 10MB 切割
'maxLogFiles' => 100, // 保留最近 20 个文件
// 核心:自动添加 ID 前缀
'prefix' => function ($message) {
$reqId = Yii::$app->params['request_id'] ?? '-';
$ip = Yii::$app->request->getUserIP() ?? '-';
$userId = Yii::$app->has('user', true) && Yii::$app->user->id ? Yii::$app->user->id : '-';
// 输出格式: [RequestID][IP][UserID]
return "[$reqId][$ip][$userId]";
},
],
// 3. 第三方 API 请求日志
[
'class' => 'yii\log\FileTarget',
'categories' => ['external_api'],
'logFile' => '@runtime/logs/api_trace' . date('Y-m-d') . '.log',
'logVars' => [],
],
],
],
],
效果:
开发人员调用 Yii::info('Payment started'),日志文件中实际显示:
2023-10-27 10:00:01 [info] [application] [5f9a1b2c3d4e][192.168.1.1][10086] Payment started
由于上述配置会导致日志文件无限累积,如果磁盘空间有限,建议添加一个 Crontab 任务来清理 N 天前的日志。
Crontab 示例 (保留最近 180 天), 每天凌晨 3 点清理 180 天前的日志:
0 3 * * * find /path/to/project/runtime/logs -name "app-*.log*" -mtime +180 -delete
⚠️ 注意:对于 常驻内存 的应用(如 yii2-queue 的监听进程),直接在配置写 date('Y-m-d') 是无效的。因为配置只在进程启动时加载一次,日志文件名会永远停留在“启动那天”。
这是日志筛选的灵魂。格式建议为:模块名.子模块。
// ❌ 错误示范:没有 category,甚至不知道这日志是哪来的
Yii::info('Payment success: ' . $orderId);
// ✅ 正确示范:清晰的分类
Yii::info("Order {$orderId} paid successfully via WeChat.", 'payment');
// ✅ 外部 API 调用记录
Yii::info("Requesting Aleph2 API: {$url}", 'external_api');
不要拼接大段字符串,如果条件允许,尽量记录数组或 JSON,便于日志分析系统(ELK)解析。
// ✅ 推荐:记录数组
Yii::warning([
'msg' => 'Invalid Login Attempt',
'username' => $username,
'ip' => $ip,
'reason' => 'Password mismatch'
], 'security.auth');
永远不要只记录 Exception 的 Message,要记录完整的 Exception 对象,Yii 会自动格式化它(包含堆栈信息)。
try {
// 业务逻辑
} catch (\Exception $e) {
// ✅ Yii 会自动把堆栈信息格式化好记录下来
Yii::error($e, 'order.checkout');
}
这是 Yii2 日志高性能的秘密。
flushInterval (Logger层): 内存中积累多少条消息后,触发 flush 到 Target。默认 1000。exportInterval (Target层): Target 积累多少条消息后,真正执行 export (如写盘)。默认 1000。建议:
默认情况下,FileTarget 会记录 $_GET, $_POST, $_FILES, $_COOKIE, $_SESSION, $_SERVER。
logVars => []。这些信息极其庞大,既浪费 I/O,又可能泄露用户隐私(如 Cookie、Password)。为了防止单个日志文件无限膨胀占满磁盘:
'maxFileSize' => 10240, // KB,即 10MB。生产环境建议 10MB - 100MB
'maxLogFiles' => 50, //保留历史日志文件数
当 app.log 达到 10MB 时,会自动更名为 app.log.1,旧的 app.log.1 变为 app.log.2,以此类推。
本文由 best 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。