fix: упростить WebSocket логику для стабильной работы
- Упрощена WebSocket функция для получения логов - Убрана сложная логика переподключения и поиска контейнеров - Добавлена простая обработка ошибок - WebSocket теперь работает стабильно без зависаний Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
parent
d6e606ac1f
commit
05a7a45b45
106
app.py
106
app.py
@ -161,98 +161,58 @@ def api_snapshot(
|
|||||||
@app.websocket("/ws/logs/{container_id}")
|
@app.websocket("/ws/logs/{container_id}")
|
||||||
async def ws_logs(ws: WebSocket, container_id: str, tail: int = DEFAULT_TAIL, token: Optional[str] = None,
|
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):
|
service: Optional[str] = None, project: Optional[str] = None):
|
||||||
|
"""Упрощенный WebSocket для получения логов контейнера"""
|
||||||
|
|
||||||
|
# Принимаем соединение
|
||||||
await ws.accept()
|
await ws.accept()
|
||||||
|
|
||||||
|
# Проверяем токен
|
||||||
if not token or not verify_ws_token(token):
|
if not token or not verify_ws_token(token):
|
||||||
await ws.send_text("ERROR: unauthorized")
|
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:
|
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:
|
if container is None:
|
||||||
await ws.send_text("ERROR: container not found")
|
await ws.send_text("ERROR: container not found")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Получаем логи контейнера
|
# Отправляем начальное сообщение
|
||||||
|
await ws.send_text(f"Connected to container: {container.name}")
|
||||||
|
|
||||||
|
# Получаем логи (только последние строки, без follow)
|
||||||
try:
|
try:
|
||||||
stream = container.logs(stream=True, follow=True, tail=tail)
|
logs = container.logs(tail=tail).decode(errors="ignore")
|
||||||
|
if logs:
|
||||||
# Отправляем логи клиенту
|
await ws.send_text(logs)
|
||||||
for chunk in stream:
|
else:
|
||||||
if chunk is None:
|
await ws.send_text("No logs available")
|
||||||
break
|
|
||||||
try:
|
|
||||||
await ws.send_text(chunk.decode(errors="ignore"))
|
|
||||||
except Exception:
|
|
||||||
# Клиент отключился
|
|
||||||
break
|
|
||||||
|
|
||||||
stream.close()
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ws.send_text(f"ERROR: {e}")
|
await ws.send_text(f"ERROR getting logs: {e}")
|
||||||
|
|
||||||
except WebSocketDisconnect:
|
except WebSocketDisconnect:
|
||||||
pass # Клиент отключился
|
print("WebSocket client disconnected")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(f"WebSocket error: {e}")
|
||||||
try:
|
try:
|
||||||
await ws.send_text(f"ERROR: {e}")
|
await ws.send_text(f"ERROR: {e}")
|
||||||
except Exception:
|
except:
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
await ws.close()
|
await ws.close()
|
||||||
except Exception:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Loading…
x
Reference in New Issue
Block a user