v0.1.4-p5
parent
3622117d85
commit
a6ec2368f4
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue