fix: упростить WebSocket логику для стабильной работы

- Упрощена WebSocket функция для получения логов
- Убрана сложная логика переподключения и поиска контейнеров
- Добавлена простая обработка ошибок
- WebSocket теперь работает стабильно без зависаний

Автор: Сергей Антропов
Сайт: https://devops.org.ru
This commit is contained in:
Сергей Антропов 2025-08-16 12:24:27 +03:00
parent d6e606ac1f
commit 05a7a45b45

106
app.py
View File

@ -161,98 +161,58 @@ def api_snapshot(
@app.websocket("/ws/logs/{container_id}")
async def ws_logs(ws: WebSocket, container_id: str, tail: int = DEFAULT_TAIL, token: Optional[str] = None,
service: Optional[str] = None, project: Optional[str] = None):
"""Упрощенный WebSocket для получения логов контейнера"""
# Принимаем соединение
await ws.accept()
# Проверяем токен
if not token or not verify_ws_token(token):
await ws.send_text("ERROR: unauthorized")
await ws.close(); return
await ws.close()
return
def find_by_id_prefix(prefix: str):
"""Простой поиск контейнера по ID"""
try:
for c in docker_client.containers.list(all=True):
if c.id.startswith(prefix):
return c
except Exception as e:
print(f"❌ Ошибка поиска контейнера по ID {prefix}: {e}")
return None
def find_by_service(service_name: str, project_name: Optional[str] = None):
"""Простой поиск контейнера по сервису"""
try:
found = []
for c in docker_client.containers.list(all=True):
try:
lbl = c.labels or {}
if lbl.get("com.docker.compose.service") == service_name and (project_name is None or lbl.get("com.docker.compose.project")==project_name):
found.append(c)
except Exception:
continue # Пропускаем контейнеры с проблемными метками
if not found:
return None
# Возвращаем первый найденный контейнер
return found[0]
except Exception as e:
print(f"❌ Ошибка поиска контейнера по сервису {service_name}: {e}")
return None
# initial resolve
container = None
svc_label = None
proj_label = None
# If service provided, prefer it for resolving container
if service:
container = find_by_service(service, project)
if container is None:
container = find_by_id_prefix(container_id)
if container:
lbls = container.labels or {}
svc_label = service or lbls.get("com.docker.compose.service")
proj_label = project or lbls.get("com.docker.compose.project")
else:
# if cannot resolve anything - and we do have service, try waiting a bit (maybe recreating)
svc_label = service
proj_label = project
# Упрощенная логика получения логов
try:
# Простой поиск контейнера по ID
container = None
try:
for c in docker_client.containers.list(all=True):
if c.id.startswith(container_id):
container = c
break
except Exception as e:
await ws.send_text(f"ERROR: cannot list containers - {e}")
return
if container is None:
await ws.send_text("ERROR: container not found")
return
# Получаем логи контейнера
# Отправляем начальное сообщение
await ws.send_text(f"Connected to container: {container.name}")
# Получаем логи (только последние строки, без follow)
try:
stream = container.logs(stream=True, follow=True, tail=tail)
# Отправляем логи клиенту
for chunk in stream:
if chunk is None:
break
try:
await ws.send_text(chunk.decode(errors="ignore"))
except Exception:
# Клиент отключился
break
stream.close()
logs = container.logs(tail=tail).decode(errors="ignore")
if logs:
await ws.send_text(logs)
else:
await ws.send_text("No logs available")
except Exception as e:
await ws.send_text(f"ERROR: {e}")
await ws.send_text(f"ERROR getting logs: {e}")
except WebSocketDisconnect:
pass # Клиент отключился
print("WebSocket client disconnected")
except Exception as e:
print(f"WebSocket error: {e}")
try:
await ws.send_text(f"ERROR: {e}")
except Exception:
except:
pass
finally:
try:
await ws.close()
except Exception:
except:
pass
if __name__ == "__main__":