fix(logging): replace stdlib-style exc_info kwargs with loguru opt(exception=)

Loguru has no exc_info kwarg: extra kwargs are str.format() arguments, so
logger.error(f"...{e}", exc_info=True) forces .format() on the rendered
message — any error text containing braces (e.g. a JSON error body) raises
KeyError FROM the log call itself, escaping the surrounding except block and
killing the caller (observed: whole agent turns dying in benchmark runs).
The intended traceback was also never logged, since exc_info is not a loguru
feature.

Batch fix of all 143 sites across 11 files:
  logger.X(msg, exc_info=True) -> logger.opt(exception=True).X(msg)
  (one exc_info=exc site -> opt(exception=exc))
Messages are byte-identical; with the kwarg gone loguru never calls
.format(), so brace-containing f-string messages are inert.

Verified: AST post-conditions per file, py_compile, import smoke of all
modules, behavioral equivalence of the 3 patterns, full unit suite (1549
passed) with a failure set identical to the pristine tree (22 pre-existing,
zero regressions).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Also add pixel-agents to the README acknowledgements.
This commit is contained in:
LZH-YS1998
2026-07-03 17:09:11 +08:00
parent d78931979d
commit 08e48c2f9c
12 changed files with 156 additions and 172 deletions
+3 -3
View File
@@ -77,7 +77,7 @@ class ProjectService:
if asyncio.iscoroutine(maybe):
await maybe
except Exception:
logger.debug(f"Failed to close project store for {project_id}", exc_info=True)
logger.opt(exception=True).debug(f"Failed to close project store for {project_id}")
async def list(self, *, active_project_id: str | None = None) -> ServiceResult:
active = active_project_id or self.context.active_engine_project_id()
@@ -247,7 +247,7 @@ class ProjectService:
try:
await active_engine.store.close()
except Exception:
logger.debug("Failed to close active project store before delete", exc_info=True)
logger.opt(exception=True).debug("Failed to close active project store before delete")
shutil.rmtree(str(projects_dir), ignore_errors=True)
workplace = self.context.project_workplace(project_id)
@@ -263,7 +263,7 @@ class ProjectService:
if asyncio.iscoroutine(maybe):
await maybe
except Exception:
logger.debug(f"memory.delete_project failed for {project_id}", exc_info=True)
logger.opt(exception=True).debug(f"memory.delete_project failed for {project_id}")
events = [ServiceEvent("project_deleted", {"project_id": project_id})]
payload: dict[str, Any] = {"project_id": project_id, "deleted_channels": deleted_channels}