本文目录导读:

- 基础方案:统一日志格式 + 文件汇聚
- 使用Monolog + PHP日志处理器
- 使用ELK/EFK栈(生产环境推荐)
- 使用日志收集服务(SaaS方案)
- 使用消息队列(高吞吐方案)
- 上下文关联:请求追踪
- 生产环境最佳实践
- 推荐技术选型
在PHP项目中实现日志聚合,通常需要结合统一日志格式、中央日志收集和可视化分析三个环节,以下是几种主流实现方案,从简单到复杂排序:
基础方案:统一日志格式 + 文件汇聚
适合单机或少量服务器的小项目。
步骤:
- 统一日志格式:推荐使用JSON格式,方便解析
- 日志存储位置:统一写入特定目录
- 日志轮转:使用logrotate管理日志文件
// 统一日志写入函数
function writeLog($level, $message, array $context = []) {
$log = [
'time' => date('Y-m-d H:i:s'),
'level' => $level,
'message' => $message,
'context' => $context,
'request_id' => uniqid('req_', true), // 关联同一请求
];
$logFile = '/var/log/myapp/app-' . date('Y-m-d') . '.log';
file_put_contents($logFile, json_encode($log) . PHP_EOL, FILE_APPEND);
}
汇聚方式:
- rsync/scp:定时同步到中央服务器
- NFS共享存储:所有服务器写入同一个NFS目录
缺点:扩展性差,不便于实时分析。
使用Monolog + PHP日志处理器
Monolog是PHP最流行的日志库,支持多种Handler。
安装:
composer require monolog/monolog
配置示例(统一发送到TCP/Syslog):
use Monolog\Logger;
use Monolog\Handler\SocketHandler;
use Monolog\Formatter\JsonFormatter;
// 创建日志通道
$log = new Logger('myapp');
// 发送到中央日志服务器的TCP端口
$handler = new SocketHandler('tcp://log-server:5000');
$handler->setFormatter(new JsonFormatter());
$log->pushHandler($handler);
// 使用
$log->info('User login', ['user_id' => 123]);
使用ELK/EFK栈(生产环境推荐)
这是企业级方案,适用于多服务器、高并发场景。
架构:
PHP应用 → Filebeat → Logstash/Elasticsearch → Kibana
实现步骤:
PHP端配置:
使用Monolog写入本地文件,推荐JSON格式。
Filebeat配置(安装在PHP服务器):
filebeat.inputs:
- type: log
paths:
- /var/log/myapp/*.log
json.keys_under_root: true
json.add_error_key: true
json.message_key: message
output.elasticsearch:
hosts: ["elasticsearch:9200"]
index: "myapp-logs-%{+yyyy.MM.dd}"
日志格式统一:
// 确保每条日志包含关键字段
$log = [
'@timestamp' => (new DateTime())->format('c'),
'app' => 'myapp',
'env' => getenv('APP_ENV'),
'level' => 'info',
'message' => 'Order created',
'order_id' => 'ORD-12345',
'user_id' => 456,
'duration_ms'=> 120,
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
'request_id' => $requestId,
];
使用日志收集服务(SaaS方案)
免运维,适合中小团队:
- Loggly:提供HTTP端点直接发送
- Papertrail:支持Syslog协议
- Datadog:功能全面的APM+日志
Papertrail示例:
// 使用Monolog的SyslogUdpHandler
use Monolog\Handler\SyslogUdpHandler;
use Monolog\Logger;
$handler = new SyslogUdpHandler('logs5.papertrailapp.com', 12345);
$handler->setFormatter(new LogglyFormatter());
$logger = new Logger('myapp');
$logger->pushHandler($handler);
使用消息队列(高吞吐方案)
适合大流量场景,避免直接写入影响性能。
流程:
PHP → RabbitMQ/Kafka → Logstash → Elasticsearch
PHP写入MQ示例(使用php-amqplib):
// 生产者
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('logs_queue', false, true, false, false);
$msg = new AMQPMessage(json_encode($logData), [
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
]);
$channel->basic_publish($msg, '', 'logs_queue');
// Logstash从queue中消费
上下文关联:请求追踪
为了解决多服务链路跟踪问题,建议为每个请求分配唯一ID:
// 在应用入口生成请求ID
$requestId = bin2hex(random_bytes(16));
// 使用Monolog的Processor自动添加
$logger->pushProcessor(function ($record) use ($requestId) {
$record['extra']['request_id'] = $requestId;
return $record;
});
生产环境最佳实践
-
日志级别规范:
- DEBUG:开发调试
- INFO:关键业务操作
- WARNING:需要注意但不影响功能
- ERROR:功能故障
- CRITICAL:系统级故障
-
敏感信息脱敏:
function maskSensitive($data) { if (isset($data['password'])) { $data['password'] = '******'; } if (isset($data['credit_card'])) { $data['credit_card'] = substr($data['credit_card'], -4); } return $data; } -
避免日志淹没:对高频日志使用采样策略
if (mt_rand(1, 100) <= 5) { // 5%采样率 $logger->info('High frequency metric', $data); } -
日志性能优化:
- 使用异步写入
- 批量发送(如每100条或每5秒发送一次)
- 大流量时使用UDP代替TCP
推荐技术选型
| 场景 | 方案 | 运维成本 | 查询能力 |
|---|---|---|---|
| 单机小项目 | 文件+tail | 低 | 弱 |
| 多服务器中型项目 | ELK/EFK | 中 | 强 |
| 微服务/容器化 | EFK+Grafana Loki | 中 | 强 |
| 不想运维 | Datadog/Loggly | 无 | 极强 |
| 极高并发 | Kafka+ELK | 高 | 强 |
建议:如果团队规模小于5人,优先考虑SaaS方案或简单的Syslog聚合;团队超过10人且有运维能力,直接上ELK/EFK栈。
选择方案时,重点考虑:日志量规模、查询需求复杂度、运维团队能力和预算。