This commit is contained in:
2026-09-27 23:06:24 +08:00
parent 2d4f9230b1
commit 22450e5bb3
40 changed files with 3040 additions and 697 deletions
+59 -26
View File
@@ -1,9 +1,8 @@
from __future__ import annotations
import json
import logging
import os
from datetime import datetime, timezone
from pathlib import Path
import json
import logging
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from app.models.agent import AgentPersona
@@ -12,28 +11,52 @@ from app.models.world import WorldMetrics
logger = logging.getLogger(__name__)
LOGS_DIR = Path(__file__).resolve().parent.parent.parent / "logs"
class SimulationLogger:
def __init__(self, sim_id: str):
self._sim_id = sim_id
LOGS_DIR.mkdir(parents=True, exist_ok=True)
self._path = LOGS_DIR / f"{sim_id}.jsonl"
try:
self._fh = open(self._path, "a", encoding="utf-8")
except OSError as exc:
logger.warning("SimulationLogger: cannot open %s: %s", self._path, exc)
self._fh = None
BACKEND_LOGS_DIR = Path(__file__).resolve().parent.parent.parent / "logs"
PROJECT_LOGS_DIR = Path(__file__).resolve().parents[3] / "logs"
class SimulationLogger:
def __init__(self, sim_id: str):
self._sim_id = sim_id
# Keep the structured per-simulation log for programmatic inspection,
# and create a human-readable text log in the project root at the
# moment the user clicks "Create World".
BACKEND_LOGS_DIR.mkdir(parents=True, exist_ok=True)
PROJECT_LOGS_DIR.mkdir(parents=True, exist_ok=True)
self._created_at = datetime.now().astimezone()
self._path = BACKEND_LOGS_DIR / f"{sim_id}.jsonl"
self.text_path = PROJECT_LOGS_DIR / (
f"logs_{self._created_at.strftime('%Y%m%d_%H%M%S_%f')}.txt"
)
try:
self._fh = open(self._path, "a", encoding="utf-8")
except OSError as exc:
logger.warning("SimulationLogger: cannot open %s: %s", self._path, exc)
self._fh = None
try:
self._text_fh = open(self.text_path, "a", encoding="utf-8")
self._text_fh.write(
f"[{self._created_at.isoformat()}] simulation_started "
f"sim_id={self._sim_id}\n"
)
self._text_fh.flush()
except OSError as exc:
logger.warning("SimulationLogger: cannot open %s: %s", self.text_path, exc)
self._text_fh = None
def _write(self, record: dict[str, Any]):
if not self._fh:
return
try:
self._fh.write(json.dumps(record, default=str) + "\n")
self._fh.flush()
except Exception as exc:
logger.debug("SimulationLogger write error: %s", exc)
try:
serialized = json.dumps(record, ensure_ascii=False, default=str)
self._fh.write(serialized + "\n")
self._fh.flush()
if self._text_fh:
ts = record.get("ts", datetime.now().astimezone().isoformat())
self._text_fh.write(f"[{ts}] {serialized}\n")
self._text_fh.flush()
except Exception as exc:
logger.debug("SimulationLogger write error: %s", exc)
def log_round(
self,
@@ -150,6 +173,16 @@ class SimulationLogger:
if self._fh:
try:
self._fh.close()
except Exception:
pass
self._fh = None
except Exception:
pass
self._fh = None
if self._text_fh:
try:
self._text_fh.write(
f"[{datetime.now().astimezone().isoformat()}] simulation_log_closed\n"
)
self._text_fh.flush()
self._text_fh.close()
except Exception:
pass
self._text_fh = None