fix(logging): replace structlog with stdlib logging to prevent broken pipe crashes

structlog fails with BrokenPipeError when stdout is redirected (e.g., background
processes, Docker logs). Replace all structlog.get_logger() calls with
logging.getLogger() and convert keyword-style log calls to %-format strings.

Also removes stale Docs/tasks.md (2028 lines) and updates Robot Framework
tests to match current API behavior.
This commit is contained in:
2026-06-21 20:14:31 +02:00
parent be3e180137
commit 572aa0fc78
18 changed files with 299 additions and 2254 deletions

View File

@@ -16,14 +16,14 @@ optional and used for display purposes only.
from __future__ import annotations
import asyncio
import logging
from collections import defaultdict
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional, Set
import structlog
from fastapi import WebSocket, WebSocketDisconnect
logger = structlog.get_logger(__name__)
logger = logging.getLogger(__name__)
class WebSocketServiceError(Exception):
@@ -96,9 +96,8 @@ class ConnectionManager:
self._connection_metadata[connection_id] = metadata or {}
logger.info(
"WebSocket connected",
connection_id=connection_id,
total_connections=len(self._active_connections),
"WebSocket connected connection_id=%s total_connections=%s",
connection_id, len(self._active_connections),
)
async def disconnect(self, connection_id: str) -> None:
@@ -122,9 +121,8 @@ class ConnectionManager:
self._connection_metadata.pop(connection_id, None)
logger.info(
"WebSocket disconnected",
connection_id=connection_id,
total_connections=len(self._active_connections),
"WebSocket disconnected connection_id=%s total_connections=%s",
connection_id, len(self._active_connections),
)
async def join_room(self, connection_id: str, room: str) -> None:
@@ -138,16 +136,13 @@ class ConnectionManager:
if connection_id in self._active_connections:
self._rooms[room].add(connection_id)
logger.debug(
"Connection joined room",
connection_id=connection_id,
room=room,
room_size=len(self._rooms[room]),
"Connection joined room connection_id=%s room=%s room_size=%s",
connection_id, room, len(self._rooms[room]),
)
else:
logger.warning(
"Attempted to join room with inactive connection",
connection_id=connection_id,
room=room,
"Attempted to join room with inactive connection connection_id=%s room=%s",
connection_id, room,
)
async def leave_room(self, connection_id: str, room: str) -> None:
@@ -166,9 +161,8 @@ class ConnectionManager:
del self._rooms[room]
logger.debug(
"Connection left room",
connection_id=connection_id,
room=room,
"Connection left room connection_id=%s room=%s",
connection_id, room,
)
async def send_personal_message(
@@ -185,26 +179,24 @@ class ConnectionManager:
try:
await websocket.send_json(message)
logger.debug(
"Personal message sent",
connection_id=connection_id,
message_type=message.get("type", "unknown"),
"Personal message sent connection_id=%s message_type=%s",
connection_id, message.get("type", "unknown"),
)
except WebSocketDisconnect:
logger.warning(
"Connection disconnected during send",
connection_id=connection_id,
"Connection disconnected during send connection_id=%s",
connection_id,
)
await self.disconnect(connection_id)
except Exception as e:
logger.error(
"Failed to send personal message",
connection_id=connection_id,
error=str(e),
"Failed to send personal message connection_id=%s error=%s",
connection_id, str(e),
)
else:
logger.warning(
"Attempted to send message to inactive connection",
connection_id=connection_id,
"Attempted to send message to inactive connection connection_id=%s",
connection_id,
)
async def broadcast(
@@ -227,15 +219,14 @@ class ConnectionManager:
await websocket.send_json(message)
except WebSocketDisconnect:
logger.warning(
"Connection disconnected during broadcast",
connection_id=connection_id,
"Connection disconnected during broadcast connection_id=%s",
connection_id,
)
disconnected.append(connection_id)
except Exception as e:
logger.error(
"Failed to broadcast to connection",
connection_id=connection_id,
error=str(e),
"Failed to broadcast to connection connection_id=%s error=%s",
connection_id, str(e),
)
# Cleanup disconnected connections
@@ -243,10 +234,10 @@ class ConnectionManager:
await self.disconnect(connection_id)
logger.debug(
"Message broadcast",
message_type=message.get("type", "unknown"),
recipient_count=len(self._active_connections) - len(exclude),
failed_count=len(disconnected),
"Message broadcast message_type=%s recipient_count=%s failed_count=%s",
message.get("type", "unknown"),
len(self._active_connections) - len(exclude),
len(disconnected),
)
async def broadcast_to_room(
@@ -270,17 +261,14 @@ class ConnectionManager:
await websocket.send_json(message)
except WebSocketDisconnect:
logger.warning(
"Connection disconnected during room broadcast",
connection_id=connection_id,
room=room,
"Connection disconnected during room broadcast connection_id=%s room=%s",
connection_id, room,
)
disconnected.append(connection_id)
except Exception as e:
logger.error(
"Failed to broadcast to room member",
connection_id=connection_id,
room=room,
error=str(e),
"Failed to broadcast to room member connection_id=%s room=%s error=%s",
connection_id, room, str(e),
)
# Cleanup disconnected connections
@@ -288,11 +276,9 @@ class ConnectionManager:
await self.disconnect(connection_id)
logger.debug(
"Message broadcast to room",
room=room,
message_type=message.get("type", "unknown"),
recipient_count=len(room_members),
failed_count=len(disconnected),
"Message broadcast to room room=%s message_type=%s recipient_count=%s failed_count=%s",
room, message.get("type", "unknown"),
len(room_members), len(disconnected),
)
async def get_connection_count(self) -> int:
@@ -604,9 +590,8 @@ class WebSocketService:
}
await self._manager.broadcast(message)
logger.info(
"Broadcast scan_started",
directory=directory,
total_items=total_items,
"Broadcast scan_started directory=%s total_items=%s",
directory, total_items,
)
async def broadcast_scan_progress(
@@ -660,10 +645,8 @@ class WebSocketService:
}
await self._manager.broadcast(message)
logger.info(
"Broadcast scan_completed",
total_directories=total_directories,
total_files=total_files,
elapsed_seconds=round(elapsed_seconds, 2),
"Broadcast scan_completed total_directories=%s total_files=%s elapsed_seconds=%s",
total_directories, total_files, round(elapsed_seconds, 2),
)
async def shutdown(self, timeout: float = 5.0) -> None: