本文目录导读:

在PHP项目中使用ELK(Elasticsearch, Logstash, Kibana)栈,主要有以下几种方式,我来为你详细说明每种方案的具体实现:
整体架构方案
PHP应用 → 日志文件/直接发送 → Logstash → Elasticsearch → Kibana
主流实现方案
方案1:使用Filebeat + Logstash(推荐)
适用于多服务器、高并发场景,性能影响最小。
步骤:
- PHP端配置日志
// 统一日志格式,推荐使用Monolog use Monolog\Logger; use Monolog\Handler\StreamHandler; use Monolog\Formatter\JsonFormatter;
$log = new Logger('app'); $handler = new StreamHandler('/var/log/php-app/app.log', Logger::INFO); $handler->setFormatter(new JsonFormatter()); $log->pushHandler($handler);
// 记录日志 $log->info('用户登录成功', [ 'user_id' => 123, 'ip' => '192.168.1.1', 'action' => 'login', 'timestamp' => time() ]);
2. **Filebeat配置** (`filebeat.yml`)
```yaml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/php-app/*.log
json.keys_under_root: true
json.overwrite_keys: true
output.logstash:
hosts: ["localhost:5044"]
- Logstash配置 (
logstash.conf)input { beats { port => 5044 } }
filter { json { source => "message" }
date { match => ["timestamp", "UNIX"] target => "@timestamp" } }
output { elasticsearch { hosts => ["localhost:9200"] index => "php-app-logs-%{+YYYY.MM.dd}" } }
### 方案2:PHP直接发送到Logstash
适用于低并发、需要实时处理的小型应用。
```php
// 使用Guzzle HTTP客户端发送
use GuzzleHttp\Client;
class ElkLogger {
private $client;
private $logstashUrl;
public function __construct($logstashUrl = 'http://localhost:5000') {
$this->client = new Client();
$this->logstashUrl = $logstashUrl;
}
public function log($level, $message, $context = []) {
$data = [
'@timestamp' => date('c'),
'level' => $level,
'message' => $message,
'context' => $context,
'app_name' => 'my-php-app',
'environment' => getenv('APP_ENV') ?: 'production',
'server_ip' => $_SERVER['SERVER_ADDR'] ?? 'unknown'
];
try {
$this->client->post($this->logstashUrl, [
'json' => $data,
'timeout' => 1 // 非阻塞
]);
} catch (\Exception $e) {
// 无法发送到ELK时,回退到文件日志
error_log("ELK Logger failed: " . $e->getMessage());
}
}
public function info($message, $context = []) {
$this->log('info', $message, $context);
}
public function error($message, $context = []) {
$this->log('error', $message, $context);
}
}
// 使用示例
$logger = new ElkLogger();
$logger->info('用户注册成功', ['user_id' => 456, 'email' => 'test@example.com']);
Logstash HTTP输入配置:
input {
http {
port => 5000
codec => json
}
}
filter {
# 处理时间戳
date {
match => ["@timestamp", "ISO8601"]
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "php-app-logs-%{+YYYY.MM.dd}"
}
}
方案3:使用PHP Elasticsearch客户端直接写入
适用于需要精细化控制的场景。
// 安装: composer require elasticsearch/elasticsearch
use Elasticsearch\ClientBuilder;
class ElasticsearchLogger {
private $client;
private $index;
public function __construct($hosts = ['localhost:9200'], $index = 'php-app-logs') {
$this->client = ClientBuilder::create()
->setHosts($hosts)
->build();
$this->index = $index;
}
public function log($level, $message, $context = []) {
$params = [
'index' => $this->index . '-' . date('Y.m.d'),
'body' => [
'@timestamp' => date('c'),
'level' => $level,
'message' => $message,
'context' => $context,
'app_name' => 'my-php-app',
'environment' => getenv('APP_ENV') ?: 'production'
]
];
try {
$response = $this->client->index($params);
return $response['_id'];
} catch (\Exception $e) {
error_log("Elasticsearch Logger failed: " . $e->getMessage());
return false;
}
}
// 批量写入,提高性能
public function batchLog($entries) {
$params = ['body' => []];
foreach ($entries as $entry) {
$params['body'][] = [
'index' => [
'_index' => $this->index . '-' . date('Y.m.d')
]
];
$params['body'][] = [
'@timestamp' => $entry['timestamp'] ?? date('c'),
'level' => $entry['level'],
'message' => $entry['message'],
'context' => $entry['context'] ?? []
];
}
try {
return $this->client->bulk($params);
} catch (\Exception $e) {
error_log("Elasticsearch bulk failed: " . $e->getMessage());
return false;
}
}
}
Monolog集成方案(最推荐)
Monolog是PHP最流行的日志库,有现成的ELK集成。
安装依赖
composer require monolog/monolog
配置Monolog Handler
use Monolog\Logger;
use Monolog\Handler\ElasticsearchHandler;
use Monolog\Formatter\ElasticsearchFormatter;
use Elasticsearch\ClientBuilder;
// 创建Elasticsearch客户端
$client = ClientBuilder::create()
->setHosts(['localhost:9200'])
->build();
// 创建日志
$logger = new Logger('my_app');
// Elasticsearch Handler
$options = [
'index' => 'monolog-' . date('Y.m.d'),
'type' => 'log'
];
$handler = new ElasticsearchHandler($client, $options);
$handler->setFormatter(new ElasticsearchFormatter('monolog-' . date('Y.m.d'), 'log'));
$logger->pushHandler($handler);
// 使用
$logger->info('用户行为记录', [
'user_id' => 123,
'action' => 'purchase',
'amount' => 99.99
]);
$logger->error('数据库错误', [
'sql' => 'SELECT * FROM users',
'error_code' => 1045
]);
Logstash格式化建议
确保PHP日志包含这些关键字段:
// 推荐的日志结构
[
'timestamp' => date('c'),
'level' => 'INFO',
'message' => '日志内容',
'app_name' => 'my-php-app',
'environment' => 'production',
'request_id' => uniqid(),
'user_id' => $userId,
'ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
'uri' => $_SERVER['REQUEST_URI'],
'method' => $_SERVER['REQUEST_METHOD'],
'duration' => $endTime - $startTime, // 请求耗时
'extra' => [] // 自定义数据
]
Kibana可视化配置
在Kibana中创建索引模式:
- 打开 Kibana → Management → Index Patterns
- 创建
php-app-logs-*索引模式 - 配置时间字段为
@timestamp
常用可视化建议:
- 应用错误率仪表板:根据 level=ERROR 过滤
- API响应时间分析:使用 duration 字段
- 用户行为追踪:根据 user_id 和 action 字段
- 错误追踪:按 error_code 和 message 聚合
性能优化建议
- 使用缓冲写入:批量写入而非单条写入
- 异步处理:使用消息队列如RabbitMQ
- 日志级别控制:生产环境只记录WARNING及以上级别
- 日志采样:高并发场景下只记录部分请求
- 索引生命周期管理:定期删除旧索引
对于大多数PHP项目,推荐使用 方案1(Filebeat+Logstash) 或 Monolog集成方案,它们:
- 对PHP应用性能影响最小
- 易于维护和扩展
- 支持高并发场景
- 提供更好的错误隔离
选择哪种方案主要取决于你的具体需求:实时性、并发量、团队技术栈等。