Files
toolbox/app/Clients/SlsClient.php

293 lines
8.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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_* 配置项');
}
}
}