refactor: standardize booter structured logging format

This commit is contained in:
zenfun
2026-03-11 03:00:37 +08:00
parent ad3911a21f
commit e1d76117b4
3 changed files with 106 additions and 38 deletions
+56 -14
View File
@@ -71,7 +71,7 @@ class MockShipyardSandboxClient:
async with session.post(url, data=data) as response:
if response.status == 200:
logger.info(
"[Computer] File uploaded to Boxlite sandbox: %s",
"[Computer] file_upload booter=boxlite remote_path=%s",
remote_path,
)
return {
@@ -81,6 +81,11 @@ class MockShipyardSandboxClient:
}
else:
error_text = await response.text()
logger.warning(
"[Computer] file_upload_failed booter=boxlite error=http_status status=%s remote_path=%s",
response.status,
remote_path,
)
return {
"success": False,
"error": f"Server returned {response.status}: {error_text}",
@@ -88,30 +93,39 @@ class MockShipyardSandboxClient:
}
except aiohttp.ClientError as e:
logger.error(f"Failed to upload file: {e}")
logger.error("[Computer] file_upload_failed booter=boxlite error=%s", e)
return {
"success": False,
"error": f"Connection error: {str(e)}",
"message": "File upload failed",
}
except asyncio.TimeoutError:
logger.warning(
"[Computer] file_upload_failed booter=boxlite error=timeout remote_path=%s",
remote_path,
)
return {
"success": False,
"error": "File upload timeout",
"message": "File upload failed",
}
except FileNotFoundError:
logger.error(f"File not found: {path}")
logger.error(
"[Computer] file_upload_failed booter=boxlite error=file_not_found path=%s",
path,
)
return {
"success": False,
"error": f"File not found: {path}",
"message": "File upload failed",
}
except Exception as e:
logger.error(f"Unexpected error uploading file: {e}")
except Exception as exc:
logger.exception(
"[Computer] file_upload_failed booter=boxlite error=unexpected"
)
return {
"success": False,
"error": f"Internal error: {str(e)}",
"error": f"Internal error: {str(exc)}",
"message": "File upload failed",
}
@@ -120,24 +134,42 @@ class MockShipyardSandboxClient:
loop = 60
while loop > 0:
try:
logger.info(
f"Checking health for sandbox {ship_id} on {self.sb_url}..."
logger.debug(
"[Computer] health_check booter=boxlite ship_id=%s session=%s endpoint=%s attempt=%s healthy=pending",
ship_id,
session_id,
self.sb_url,
61 - loop,
)
url = f"{self.sb_url}/health"
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
logger.info(f"Sandbox {ship_id} is healthy")
return
logger.debug(
"[Computer] health_check booter=boxlite ship_id=%s session=%s endpoint=%s healthy=true",
ship_id,
session_id,
self.sb_url,
)
return
await asyncio.sleep(1)
loop -= 1
except Exception:
await asyncio.sleep(1)
loop -= 1
logger.warning(
"[Computer] health_check_timeout booter=boxlite ship_id=%s session=%s endpoint=%s",
ship_id,
session_id,
self.sb_url,
)
class BoxliteBooter(ComputerBooter):
async def boot(self, session_id: str) -> None:
logger.info(
f"Booting(Boxlite) for session: {session_id}, this may take a while..."
"[Computer] booter_boot booter=boxlite session=%s status=starting",
session_id,
)
random_port = random.randint(20000, 30000)
self.box = boxlite.SimpleBox(
@@ -152,7 +184,11 @@ class BoxliteBooter(ComputerBooter):
],
)
await self.box.start()
logger.info(f"Boxlite booter started for session: {session_id}")
logger.info(
"[Computer] booter_boot booter=boxlite session=%s status=ready ship_id=%s",
session_id,
self.box.id,
)
self.mocked = MockShipyardSandboxClient(
sb_url=f"http://127.0.0.1:{random_port}"
)
@@ -175,9 +211,15 @@ class BoxliteBooter(ComputerBooter):
await self.mocked.wait_healthy(self.box.id, session_id)
async def shutdown(self) -> None:
logger.info(f"Shutting down Boxlite booter for ship: {self.box.id}")
logger.info(
"[Computer] booter_shutdown booter=boxlite ship_id=%s status=starting",
self.box.id,
)
self.box.shutdown()
logger.info(f"Boxlite booter for ship: {self.box.id} stopped")
logger.info(
"[Computer] booter_shutdown booter=boxlite ship_id=%s status=done",
self.box.id,
)
@property
def fs(self) -> FileSystemComponent:
+20 -10
View File
@@ -56,11 +56,15 @@ class ShipyardBooter(ComputerBooter):
max_session_num=self._session_num,
session_id=session_id,
)
logger.info(f"Got sandbox ship: {ship.id} for session: {session_id}")
logger.info(
"[Computer] sandbox_created booter=shipyard ship_id=%s session=%s",
ship.id,
session_id,
)
self._ship = ship
async def shutdown(self) -> None:
logger.info("[Computer] Shipyard booter shutdown.")
logger.info("[Computer] booter_shutdown booter=shipyard status=done")
@property
def fs(self) -> FileSystemComponent:
@@ -77,14 +81,17 @@ class ShipyardBooter(ComputerBooter):
async def upload_file(self, path: str, file_name: str) -> dict:
"""Upload file to sandbox"""
result = await self._ship.upload_file(path, file_name)
logger.info("[Computer] File uploaded to Shipyard sandbox: %s", file_name)
logger.info(
"[Computer] file_upload booter=shipyard remote_path=%s",
file_name,
)
return result
async def download_file(self, remote_path: str, local_path: str):
"""Download file from sandbox."""
result = await self._ship.download_file(remote_path, local_path)
logger.info(
"[Computer] File downloaded from Shipyard sandbox: %s -> %s",
"[Computer] file_download booter=shipyard remote_path=%s local_path=%s",
remote_path,
local_path,
)
@@ -96,18 +103,21 @@ class ShipyardBooter(ComputerBooter):
ship_id = self._ship.id
data = await self._sandbox_client.get_ship(ship_id)
if not data:
logger.info(
"[Computer] Shipyard sandbox health check: id=%s, healthy=False (no data)",
logger.debug(
"[Computer] health_check booter=shipyard ship_id=%s healthy=false reason=no_data",
ship_id,
)
return False
health = bool(data.get("status", 0) == 1)
logger.info(
"[Computer] Shipyard sandbox health check: id=%s, healthy=%s",
logger.debug(
"[Computer] health_check booter=shipyard ship_id=%s healthy=%s",
ship_id,
health,
)
return health
except Exception as e:
logger.error(f"Error checking Shipyard sandbox availability: {e}")
except Exception:
logger.exception(
"[Computer] health_check_failed booter=shipyard ship_id=%s",
getattr(getattr(self, "_ship", None), "id", "unknown"),
)
return False
+30 -14
View File
@@ -319,14 +319,17 @@ class ShipyardNeoBooter(ComputerBooter):
if self._bay_manager is not None:
await self._bay_manager.close_client()
logger.info("[Computer] Neo auto-start mode: launching Bay container")
logger.info("[Computer] bay_autostart status=starting")
self._bay_manager = BayContainerManager()
self._endpoint_url = await self._bay_manager.ensure_running()
await self._bay_manager.wait_healthy()
# Read auto-provisioned credentials
if not self._access_token:
self._access_token = await self._bay_manager.read_credentials()
logger.info("[Computer] Bay auto-started at %s", self._endpoint_url)
logger.info(
"[Computer] bay_autostart status=ready endpoint=%s",
self._endpoint_url,
)
if not self._endpoint_url or not self._access_token:
if self._bay_manager is not None:
@@ -366,7 +369,7 @@ class ShipyardNeoBooter(ComputerBooter):
)
logger.info(
"Got Shipyard Neo sandbox: %s (profile=%s, capabilities=%s, auto=%s)",
"[Computer] sandbox_created booter=shipyard_neo sandbox_id=%s profile=%s capabilities=%s auto=%s",
self._sandbox.id,
resolved_profile,
list(caps),
@@ -388,7 +391,10 @@ class ShipyardNeoBooter(ComputerBooter):
"""
# User explicitly set a profile → honour it
if self._profile and self._profile != self.DEFAULT_PROFILE:
logger.info("[Computer] Using user-specified profile: %s", self._profile)
logger.info(
"[Computer] profile_selected mode=user profile=%s",
self._profile,
)
return self._profile
# Query Bay for available profiles
@@ -401,7 +407,7 @@ class ShipyardNeoBooter(ComputerBooter):
raise # auth errors must not be silenced
except Exception as exc:
logger.warning(
"[Computer] Failed to query Bay profiles, falling back to %s: %s",
"[Computer] profile_selection_fallback reason=query_failed fallback=%s error=%s",
self.DEFAULT_PROFILE,
exc,
)
@@ -421,7 +427,7 @@ class ShipyardNeoBooter(ComputerBooter):
if chosen != self.DEFAULT_PROFILE:
caps = getattr(best, "capabilities", [])
logger.info(
"[Computer] Auto-selected profile %s (capabilities=%s)",
"[Computer] profile_selected mode=auto profile=%s capabilities=%s",
chosen,
caps,
)
@@ -432,12 +438,16 @@ class ShipyardNeoBooter(ComputerBooter):
if self._client is not None:
sandbox_id = getattr(self._sandbox, "id", "unknown")
logger.info(
"[Computer] Shutting down Shipyard Neo sandbox: id=%s", sandbox_id
"[Computer] booter_shutdown booter=shipyard_neo sandbox_id=%s status=starting",
sandbox_id,
)
await self._client.__aexit__(None, None, None)
self._client = None
self._sandbox = None
logger.info("[Computer] Shipyard Neo sandbox shut down: id=%s", sandbox_id)
logger.info(
"[Computer] booter_shutdown booter=shipyard_neo sandbox_id=%s status=done",
sandbox_id,
)
# NOTE: We intentionally do NOT stop the Bay container here.
# It stays running for reuse by future sessions. The user can
@@ -476,7 +486,10 @@ class ShipyardNeoBooter(ComputerBooter):
content = f.read()
remote_path = file_name.lstrip("/")
await self._sandbox.filesystem.upload(remote_path, content)
logger.info("[Computer] File uploaded to Neo sandbox: %s", remote_path)
logger.info(
"[Computer] file_upload booter=shipyard_neo remote_path=%s",
remote_path,
)
return {
"success": True,
"message": "File uploaded successfully",
@@ -493,7 +506,7 @@ class ShipyardNeoBooter(ComputerBooter):
with open(local_path, "wb") as f:
f.write(cast(bytes, content))
logger.info(
"[Computer] File downloaded from Neo sandbox: %s -> %s",
"[Computer] file_download booter=shipyard_neo remote_path=%s local_path=%s",
remote_path,
local_path,
)
@@ -505,15 +518,18 @@ class ShipyardNeoBooter(ComputerBooter):
await self._sandbox.refresh()
status = getattr(self._sandbox.status, "value", str(self._sandbox.status))
healthy = status not in {"failed", "expired"}
logger.info(
"[Computer] Neo sandbox health check: id=%s, status=%s, healthy=%s",
logger.debug(
"[Computer] health_check booter=shipyard_neo sandbox_id=%s status=%s healthy=%s",
getattr(self._sandbox, "id", "unknown"),
status,
healthy,
)
return healthy
except Exception as e:
logger.error(f"Error checking Shipyard Neo sandbox availability: {e}")
except Exception:
logger.exception(
"[Computer] health_check_failed booter=shipyard_neo sandbox_id=%s",
getattr(self._sandbox, "id", "unknown"),
)
return False
# ── Tool / prompt self-description ────────────────────────────