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 (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_* 配置项'); } } }