v0.1.4-p5

main
mula.liu 2026-04-13 21:25:25 +08:00
parent 3622117d85
commit a6ec2368f4
3 changed files with 23 additions and 37 deletions

View File

@ -27,13 +27,6 @@ class BotDockerManager:
self._storage_limit_supported: Optional[bool] = None self._storage_limit_supported: Optional[bool] = None
self._storage_limit_warning_emitted = False 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 @staticmethod
def _normalize_resource_limits( def _normalize_resource_limits(
cpu_cores: Optional[float], cpu_cores: Optional[float],
@ -628,22 +621,7 @@ class BotDockerManager:
text = str(raw or "") text = str(raw or "")
return [line for line in text.splitlines() if line.strip()] 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]: def _read_log_lines(self, bot_id: str, tail: Optional[int] = None) -> 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
if not self.client: if not self.client:
return [] return []
try: try:
@ -652,8 +630,8 @@ class BotDockerManager:
print(f"[DockerManager] Error reading logs for {bot_id}: {e}") print(f"[DockerManager] Error reading logs for {bot_id}: {e}")
return [] return []
def get_recent_logs(self, bot_id: str, tail: int = 300, *, fresh_client: bool = False) -> List[str]: 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)), fresh_client=fresh_client) return self._read_log_lines(bot_id, tail=max(1, int(tail)))
def get_logs_page( def get_logs_page(
self, self,
@ -661,8 +639,6 @@ class BotDockerManager:
offset: int = 0, offset: int = 0,
limit: int = 50, limit: int = 50,
reverse: bool = True, reverse: bool = True,
*,
fresh_client: bool = False,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
safe_offset = max(0, int(offset)) safe_offset = max(0, int(offset))
safe_limit = max(1, int(limit)) safe_limit = max(1, int(limit))
@ -670,7 +646,7 @@ class BotDockerManager:
# Docker logs API supports tail but not arbitrary offsets. For reverse pagination # Docker logs API supports tail but not arbitrary offsets. For reverse pagination
# we only read the minimal newest slice needed for the requested page. # we only read the minimal newest slice needed for the requested page.
tail_count = safe_offset + safe_limit + 1 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)) ordered = list(reversed(lines))
page = ordered[safe_offset:safe_offset + safe_limit] page = ordered[safe_offset:safe_offset + safe_limit]
has_more = len(lines) > safe_offset + safe_limit has_more = len(lines) > safe_offset + safe_limit
@ -683,7 +659,7 @@ class BotDockerManager:
"reverse": reverse, "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) total = len(lines)
page = lines[safe_offset:safe_offset + safe_limit] page = lines[safe_offset:safe_offset + safe_limit]
return { return {

View File

@ -84,13 +84,12 @@ def get_bot_logs(
offset=max(0, int(offset)), offset=max(0, int(offset)),
limit=max(1, int(limit)), limit=max(1, int(limit)),
reverse=bool(reverse), reverse=bool(reverse),
fresh_client=True,
) )
return {"bot_id": bot_id, **page} return {"bot_id": bot_id, **page}
effective_tail = max(1, int(tail or 300)) effective_tail = max(1, int(tail or 300))
return { return {
"bot_id": bot_id, "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),
} }

View File

@ -62,6 +62,7 @@ export function usePlatformBotDockerLogs({
const selectedBotId = String(selectedBotInfo?.id || '').trim(); const selectedBotId = String(selectedBotInfo?.id || '').trim();
const [dockerLogs, setDockerLogs] = useState<string[]>([]); const [dockerLogs, setDockerLogs] = useState<string[]>([]);
const [dockerLogsLoading, setDockerLogsLoading] = useState(false); const [dockerLogsLoading, setDockerLogsLoading] = useState(false);
const [dockerLogsReady, setDockerLogsReady] = useState(false);
const [dockerLogsError, setDockerLogsError] = useState(''); const [dockerLogsError, setDockerLogsError] = useState('');
const [dockerLogsPage, setDockerLogsPage] = useState(1); const [dockerLogsPage, setDockerLogsPage] = useState(1);
const [dockerLogsHasMore, setDockerLogsHasMore] = useState(false); const [dockerLogsHasMore, setDockerLogsHasMore] = useState(false);
@ -79,6 +80,7 @@ export function usePlatformBotDockerLogs({
...parseDockerLogEntry(line), ...parseDockerLogEntry(line),
})); }));
if (logs.length > 0) return logs; if (logs.length > 0) return logs;
if (!dockerLogsReady || dockerLogsLoading) return [];
const events = (selectedBotInfo?.events || []) const events = (selectedBotInfo?.events || [])
.filter((event) => String(event?.text || '').trim().length > 0) .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()}`), ...parseDockerLogEntry(`[${String(event.state || 'INFO').toUpperCase()}] ${String(event.text || '').trim()}`),
})); }));
return events; return events;
}, [dockerLogs, dockerLogsPage, effectivePageSize, selectedBotInfo?.events]); }, [dockerLogs, dockerLogsLoading, dockerLogsPage, dockerLogsReady, effectivePageSize, selectedBotInfo?.events]);
const dockerLogTableRows = useMemo( const dockerLogTableRows = useMemo(
() => [ () => [
@ -107,6 +109,7 @@ export function usePlatformBotDockerLogs({
setDockerLogs([]); setDockerLogs([]);
setDockerLogsHasMore(false); setDockerLogsHasMore(false);
setDockerLogsError(''); setDockerLogsError('');
setDockerLogsReady(false);
setDockerLogsLoading(false); setDockerLogsLoading(false);
}, []); }, []);
@ -150,6 +153,7 @@ export function usePlatformBotDockerLogs({
setDockerLogs(lines); setDockerLogs(lines);
setDockerLogsHasMore(Boolean(res.data?.has_more)); setDockerLogsHasMore(Boolean(res.data?.has_more));
setDockerLogsPage(safePage); setDockerLogsPage(safePage);
setDockerLogsReady(true);
} catch (error: unknown) { } catch (error: unknown) {
if (requestSeq !== requestSeqRef.current || activeBotIdRef.current !== botId) { if (requestSeq !== requestSeqRef.current || activeBotIdRef.current !== botId) {
return; return;
@ -161,6 +165,7 @@ export function usePlatformBotDockerLogs({
setDockerLogs([]); setDockerLogs([]);
setDockerLogsHasMore(false); setDockerLogsHasMore(false);
setDockerLogsError(detail || (isZh ? '读取 Docker 日志失败。' : 'Failed to load Docker logs.')); setDockerLogsError(detail || (isZh ? '读取 Docker 日志失败。' : 'Failed to load Docker logs.'));
setDockerLogsReady(true);
} finally { } finally {
if (!silent && requestSeq === requestSeqRef.current && activeBotIdRef.current === botId) { if (!silent && requestSeq === requestSeqRef.current && activeBotIdRef.current === botId) {
setDockerLogsLoading(false); setDockerLogsLoading(false);
@ -177,20 +182,26 @@ export function usePlatformBotDockerLogs({
return; return;
} }
if (logsScopeKeyRef.current !== nextLogsScopeKey) { const scopeChanged = logsScopeKeyRef.current !== nextLogsScopeKey;
if (scopeChanged) {
logsScopeKeyRef.current = nextLogsScopeKey; logsScopeKeyRef.current = nextLogsScopeKey;
requestSeqRef.current += 1; requestSeqRef.current += 1;
setDockerLogs([]); setDockerLogs([]);
setDockerLogsHasMore(false); setDockerLogsHasMore(false);
setDockerLogsError(''); setDockerLogsError('');
setDockerLogsPage(1); setDockerLogsReady(false);
return; setDockerLogsLoading(true);
if (dockerLogsPage !== 1) {
setDockerLogsPage(1);
return;
}
} }
let stopped = false; 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 () => { return () => {
stopped = true; stopped = true;
}; };