diff --git a/backend/core/docker_manager.py b/backend/core/docker_manager.py index a57cbba..a4cb6c6 100644 --- a/backend/core/docker_manager.py +++ b/backend/core/docker_manager.py @@ -27,13 +27,6 @@ class BotDockerManager: self._storage_limit_supported: Optional[bool] = None self._storage_limit_warning_emitted = False - def _new_short_lived_client(self): - try: - return docker.from_env(timeout=6) - except Exception as e: - print(f"[DockerManager] failed to create short-lived client: {e}") - return None - @staticmethod def _normalize_resource_limits( cpu_cores: Optional[float], @@ -628,22 +621,7 @@ class BotDockerManager: text = str(raw or "") return [line for line in text.splitlines() if line.strip()] - def _read_log_lines(self, bot_id: str, tail: Optional[int] = None, *, fresh_client: bool = False) -> List[str]: - if fresh_client: - client = self._new_short_lived_client() - if not client: - return [] - try: - return self._read_log_lines_with_client(client, bot_id, tail=tail) - except Exception as e: - print(f"[DockerManager] Error reading logs for {bot_id} with short-lived client: {e}") - return [] - finally: - try: - client.close() - except Exception: - pass - + def _read_log_lines(self, bot_id: str, tail: Optional[int] = None) -> List[str]: if not self.client: return [] try: @@ -652,8 +630,8 @@ class BotDockerManager: print(f"[DockerManager] Error reading logs for {bot_id}: {e}") return [] - def get_recent_logs(self, bot_id: str, tail: int = 300, *, fresh_client: bool = False) -> List[str]: - return self._read_log_lines(bot_id, tail=max(1, int(tail)), fresh_client=fresh_client) + def get_recent_logs(self, bot_id: str, tail: int = 300) -> List[str]: + return self._read_log_lines(bot_id, tail=max(1, int(tail))) def get_logs_page( self, @@ -661,8 +639,6 @@ class BotDockerManager: offset: int = 0, limit: int = 50, reverse: bool = True, - *, - fresh_client: bool = False, ) -> Dict[str, Any]: safe_offset = max(0, int(offset)) safe_limit = max(1, int(limit)) @@ -670,7 +646,7 @@ class BotDockerManager: # Docker logs API supports tail but not arbitrary offsets. For reverse pagination # we only read the minimal newest slice needed for the requested page. tail_count = safe_offset + safe_limit + 1 - lines = self._read_log_lines(bot_id, tail=tail_count, fresh_client=fresh_client) + lines = self._read_log_lines(bot_id, tail=tail_count) ordered = list(reversed(lines)) page = ordered[safe_offset:safe_offset + safe_limit] has_more = len(lines) > safe_offset + safe_limit @@ -683,7 +659,7 @@ class BotDockerManager: "reverse": reverse, } - lines = self._read_log_lines(bot_id, tail=None, fresh_client=fresh_client) + lines = self._read_log_lines(bot_id, tail=None) total = len(lines) page = lines[safe_offset:safe_offset + safe_limit] return { diff --git a/backend/services/bot_runtime_service.py b/backend/services/bot_runtime_service.py index af2899a..80b08a1 100644 --- a/backend/services/bot_runtime_service.py +++ b/backend/services/bot_runtime_service.py @@ -84,13 +84,12 @@ def get_bot_logs( offset=max(0, int(offset)), limit=max(1, int(limit)), reverse=bool(reverse), - fresh_client=True, ) return {"bot_id": bot_id, **page} effective_tail = max(1, int(tail or 300)) return { "bot_id": bot_id, - "logs": docker_manager.get_recent_logs(bot_id, tail=effective_tail, fresh_client=True), + "logs": docker_manager.get_recent_logs(bot_id, tail=effective_tail), } diff --git a/frontend/src/modules/platform/hooks/usePlatformBotDockerLogs.ts b/frontend/src/modules/platform/hooks/usePlatformBotDockerLogs.ts index 03bf55b..49d5973 100644 --- a/frontend/src/modules/platform/hooks/usePlatformBotDockerLogs.ts +++ b/frontend/src/modules/platform/hooks/usePlatformBotDockerLogs.ts @@ -62,6 +62,7 @@ export function usePlatformBotDockerLogs({ const selectedBotId = String(selectedBotInfo?.id || '').trim(); const [dockerLogs, setDockerLogs] = useState([]); const [dockerLogsLoading, setDockerLogsLoading] = useState(false); + const [dockerLogsReady, setDockerLogsReady] = useState(false); const [dockerLogsError, setDockerLogsError] = useState(''); const [dockerLogsPage, setDockerLogsPage] = useState(1); const [dockerLogsHasMore, setDockerLogsHasMore] = useState(false); @@ -79,6 +80,7 @@ export function usePlatformBotDockerLogs({ ...parseDockerLogEntry(line), })); if (logs.length > 0) return logs; + if (!dockerLogsReady || dockerLogsLoading) return []; const events = (selectedBotInfo?.events || []) .filter((event) => String(event?.text || '').trim().length > 0) @@ -89,7 +91,7 @@ export function usePlatformBotDockerLogs({ ...parseDockerLogEntry(`[${String(event.state || 'INFO').toUpperCase()}] ${String(event.text || '').trim()}`), })); return events; - }, [dockerLogs, dockerLogsPage, effectivePageSize, selectedBotInfo?.events]); + }, [dockerLogs, dockerLogsLoading, dockerLogsPage, dockerLogsReady, effectivePageSize, selectedBotInfo?.events]); const dockerLogTableRows = useMemo( () => [ @@ -107,6 +109,7 @@ export function usePlatformBotDockerLogs({ setDockerLogs([]); setDockerLogsHasMore(false); setDockerLogsError(''); + setDockerLogsReady(false); setDockerLogsLoading(false); }, []); @@ -150,6 +153,7 @@ export function usePlatformBotDockerLogs({ setDockerLogs(lines); setDockerLogsHasMore(Boolean(res.data?.has_more)); setDockerLogsPage(safePage); + setDockerLogsReady(true); } catch (error: unknown) { if (requestSeq !== requestSeqRef.current || activeBotIdRef.current !== botId) { return; @@ -161,6 +165,7 @@ export function usePlatformBotDockerLogs({ setDockerLogs([]); setDockerLogsHasMore(false); setDockerLogsError(detail || (isZh ? '读取 Docker 日志失败。' : 'Failed to load Docker logs.')); + setDockerLogsReady(true); } finally { if (!silent && requestSeq === requestSeqRef.current && activeBotIdRef.current === botId) { setDockerLogsLoading(false); @@ -177,20 +182,26 @@ export function usePlatformBotDockerLogs({ return; } - if (logsScopeKeyRef.current !== nextLogsScopeKey) { + const scopeChanged = logsScopeKeyRef.current !== nextLogsScopeKey; + if (scopeChanged) { logsScopeKeyRef.current = nextLogsScopeKey; requestSeqRef.current += 1; setDockerLogs([]); setDockerLogsHasMore(false); setDockerLogsError(''); - setDockerLogsPage(1); - return; + setDockerLogsReady(false); + setDockerLogsLoading(true); + if (dockerLogsPage !== 1) { + setDockerLogsPage(1); + return; + } } let stopped = false; - void fetchDockerLogsPage(dockerLogsPage, false, selectedBotId); + const targetPage = scopeChanged ? 1 : dockerLogsPage; + void fetchDockerLogsPage(targetPage, false, selectedBotId); - if (dockerLogsPage !== 1 || String(selectedBotInfo?.docker_status || '').toUpperCase() !== 'RUNNING') { + if (targetPage !== 1 || String(selectedBotInfo?.docker_status || '').toUpperCase() !== 'RUNNING') { return () => { stopped = true; };