293 lines
8.1 KiB
PHP
293 lines
8.1 KiB
PHP
<?php
|
|
|
|
namespace App\Clients;
|
|
|
|
use Aliyun_Log_Client;
|
|
use Aliyun_Log_Models_GetLogsRequest;
|
|
use Aliyun_Log_Models_GetHistogramsRequest;
|
|
use Aliyun_Log_Exception;
|
|
use RuntimeException;
|
|
|
|
class SlsClient
|
|
{
|
|
private ?Aliyun_Log_Client $client = null;
|
|
private string $project;
|
|
private array $logstores;
|
|
|
|
public function __construct()
|
|
{
|
|
$config = config('services.sls');
|
|
|
|
if (empty($config['endpoint']) || empty($config['access_key_id']) || empty($config['access_key_secret'])) {
|
|
return;
|
|
}
|
|
|
|
$this->project = $config['project'] ?? '';
|
|
|
|
// 解析 logstore 配置,支持逗号分隔的多个 logstore
|
|
$logstoreConfig = $config['logstore'] ?? '';
|
|
if (!empty($logstoreConfig)) {
|
|
// 如果包含逗号,说明是多个 logstore
|
|
if (str_contains($logstoreConfig, ',')) {
|
|
$this->logstores = array_map('trim', explode(',', $logstoreConfig));
|
|
} else {
|
|
$this->logstores = [$logstoreConfig];
|
|
}
|
|
} else {
|
|
$this->logstores = [];
|
|
}
|
|
|
|
$this->client = new Aliyun_Log_Client(
|
|
$config['endpoint'],
|
|
$config['access_key_id'],
|
|
$config['access_key_secret'],
|
|
$config['security_token'] ?: ''
|
|
);
|
|
}
|
|
|
|
public function isConfigured(): bool
|
|
{
|
|
// 只要有 client、project,并且至少有一个 logstore 就算配置完成
|
|
return $this->client !== null
|
|
&& !empty($this->project)
|
|
&& !empty($this->logstores);
|
|
}
|
|
|
|
/**
|
|
* 获取配置的所有 logstore
|
|
*/
|
|
public function getLogstores(): array
|
|
{
|
|
return $this->logstores;
|
|
}
|
|
|
|
/**
|
|
* 获取默认的 logstore(第一个)
|
|
*/
|
|
private function getDefaultLogstore(): string
|
|
{
|
|
if (empty($this->logstores)) {
|
|
throw new RuntimeException('没有配置可用的 logstore');
|
|
}
|
|
|
|
return $this->logstores[0];
|
|
}
|
|
|
|
/**
|
|
* 查询日志
|
|
*
|
|
* @param int $from 开始时间戳
|
|
* @param int $to 结束时间戳
|
|
* @param string|null $query SLS 查询语句
|
|
* @param int $offset 偏移量
|
|
* @param int $limit 返回数量
|
|
* @param string|null $logstore 可选的 logstore,不传则使用默认
|
|
* @return array{logs: array, count: int, complete: bool}
|
|
*/
|
|
public function getLogs(
|
|
int $from,
|
|
int $to,
|
|
?string $query = null,
|
|
int $offset = 0,
|
|
int $limit = 100,
|
|
?string $logstore = null
|
|
): array {
|
|
$this->ensureConfigured();
|
|
|
|
$request = new Aliyun_Log_Models_GetLogsRequest(
|
|
$this->project,
|
|
$logstore ?? $this->getDefaultLogstore(),
|
|
$from,
|
|
$to,
|
|
'',
|
|
$query ?? '*',
|
|
$limit,
|
|
$offset,
|
|
false
|
|
);
|
|
|
|
try {
|
|
$response = $this->client->getLogs($request);
|
|
|
|
$logs = [];
|
|
foreach ($response->getLogs() as $log) {
|
|
$logs[] = $log->getContents();
|
|
}
|
|
|
|
return [
|
|
'logs' => $logs,
|
|
'count' => $response->getCount(),
|
|
'complete' => $response->isCompleted(),
|
|
];
|
|
} catch (Aliyun_Log_Exception $e) {
|
|
throw new RuntimeException(
|
|
"SLS 查询失败: [{$e->getErrorCode()}] {$e->getErrorMessage()}",
|
|
0,
|
|
$e
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取日志分布直方图
|
|
*
|
|
* @param int $from 开始时间戳
|
|
* @param int $to 结束时间戳
|
|
* @param string|null $query SLS 查询语句
|
|
* @param string|null $logstore 可选的 logstore
|
|
* @return array{histograms: array, count: int, complete: bool}
|
|
*/
|
|
public function getHistograms(
|
|
int $from,
|
|
int $to,
|
|
?string $query = null,
|
|
?string $logstore = null
|
|
): array {
|
|
$this->ensureConfigured();
|
|
|
|
$request = new Aliyun_Log_Models_GetHistogramsRequest(
|
|
$this->project,
|
|
$logstore ?? $this->getDefaultLogstore(),
|
|
$from,
|
|
$to,
|
|
'',
|
|
$query ?? '*'
|
|
);
|
|
|
|
try {
|
|
$response = $this->client->getHistograms($request);
|
|
|
|
$histograms = [];
|
|
foreach ($response->getHistograms() as $histogram) {
|
|
$histograms[] = [
|
|
'from' => $histogram->getFrom(),
|
|
'to' => $histogram->getTo(),
|
|
'count' => $histogram->getCount(),
|
|
'complete' => $histogram->isCompleted(),
|
|
];
|
|
}
|
|
|
|
return [
|
|
'histograms' => $histograms,
|
|
'count' => $response->getTotalCount(),
|
|
'complete' => $response->isCompleted(),
|
|
];
|
|
} catch (Aliyun_Log_Exception $e) {
|
|
throw new RuntimeException(
|
|
"SLS 直方图查询失败: [{$e->getErrorCode()}] {$e->getErrorMessage()}",
|
|
0,
|
|
$e
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 分页获取所有日志
|
|
*
|
|
* @param int $from 开始时间戳
|
|
* @param int $to 结束时间戳
|
|
* @param string|null $query SLS 查询语句
|
|
* @param int $maxLogs 最大返回日志数
|
|
* @param string|null $logstore 可选的 logstore
|
|
* @return array
|
|
*/
|
|
public function getAllLogs(
|
|
int $from,
|
|
int $to,
|
|
?string $query = null,
|
|
int $maxLogs = 1000,
|
|
?string $logstore = null
|
|
): array {
|
|
$allLogs = [];
|
|
$offset = 0;
|
|
$batchSize = 100;
|
|
|
|
while (count($allLogs) < $maxLogs) {
|
|
$result = $this->getLogs($from, $to, $query, $offset, $batchSize, $logstore);
|
|
|
|
$allLogs = array_merge($allLogs, $result['logs']);
|
|
|
|
if ($result['complete'] || count($result['logs']) < $batchSize) {
|
|
break;
|
|
}
|
|
|
|
$offset += $batchSize;
|
|
}
|
|
|
|
return array_slice($allLogs, 0, $maxLogs);
|
|
}
|
|
|
|
/**
|
|
* 测试连接
|
|
*/
|
|
public function testConnection(): bool
|
|
{
|
|
if (!$this->isConfigured()) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$now = time();
|
|
$this->getLogs($now - 60, $now, '*', 0, 1);
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 从多个 logstore 获取日志
|
|
*
|
|
* @param int $from 开始时间戳
|
|
* @param int $to 结束时间戳
|
|
* @param string|null $query SLS 查询语句
|
|
* @param int $maxLogs 最大返回日志数
|
|
* @param array|null $logstores 要查询的 logstore 列表,不传则使用配置的所有 logstore
|
|
* @return array 按 logstore 分组的日志数据 ['logstore_name' => ['logs' => [...], 'count' => 100]]
|
|
*/
|
|
public function getAllLogsFromMultipleStores(
|
|
int $from,
|
|
int $to,
|
|
?string $query = null,
|
|
int $maxLogs = 1000,
|
|
?array $logstores = null
|
|
): array {
|
|
$this->ensureConfigured();
|
|
|
|
$targetLogstores = $logstores ?? $this->getLogstores();
|
|
|
|
if (empty($targetLogstores)) {
|
|
throw new RuntimeException('没有配置可用的 logstore');
|
|
}
|
|
|
|
$results = [];
|
|
|
|
foreach ($targetLogstores as $logstore) {
|
|
try {
|
|
$logs = $this->getAllLogs($from, $to, $query, $maxLogs, $logstore);
|
|
$results[$logstore] = [
|
|
'logs' => $logs,
|
|
'count' => count($logs),
|
|
'success' => true,
|
|
];
|
|
} catch (\Exception $e) {
|
|
$results[$logstore] = [
|
|
'logs' => [],
|
|
'count' => 0,
|
|
'success' => false,
|
|
'error' => $e->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
private function ensureConfigured(): void
|
|
{
|
|
if (!$this->isConfigured()) {
|
|
throw new RuntimeException('SLS 客户端未配置,请检查 .env 中的 SLS_* 配置项');
|
|
}
|
|
}
|
|
}
|