| """Shared logging helpers. |
| |
| Per CLAUDE.md: |
| - Always log config + git commit ID before running anything. |
| - Use timestamps in folder names. |
| - Save more rather than less. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import json |
| import os |
| import platform |
| import subprocess |
| import sys |
| from datetime import datetime, timezone |
| from pathlib import Path |
| from typing import Any |
|
|
|
|
| def _git_commit(repo_dir: Path) -> str: |
| try: |
| out = subprocess.run( |
| ["git", "-C", str(repo_dir), "rev-parse", "HEAD"], |
| capture_output=True, |
| text=True, |
| check=True, |
| ) |
| sha = out.stdout.strip() |
| except Exception as e: |
| sha = f"<no-git: {e!s}>" |
| try: |
| dirty = subprocess.run( |
| ["git", "-C", str(repo_dir), "status", "--porcelain"], |
| capture_output=True, |
| text=True, |
| check=True, |
| ) |
| if dirty.stdout.strip(): |
| sha = f"{sha}-dirty" |
| except Exception: |
| pass |
| return sha |
|
|
|
|
| def make_run_dir(name: str, config: dict[str, Any], root: Path | None = None) -> Path: |
| """Create logs/<ts>_<name>/ with git_commit.txt, config.json, env.json. |
| |
| Returns the directory. |
| """ |
| repo = Path(__file__).resolve().parent.parent |
| root = root or (repo / "logs") |
| ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H%M%SZ") |
| run_dir = root / f"{ts}_{name}" |
| run_dir.mkdir(parents=True, exist_ok=True) |
| (run_dir / "git_commit.txt").write_text(_git_commit(repo) + "\n") |
| (run_dir / "config.json").write_text(json.dumps(config, indent=2, default=str)) |
| (run_dir / "env.json").write_text( |
| json.dumps( |
| { |
| "python": sys.version, |
| "platform": platform.platform(), |
| "argv": sys.argv, |
| "cwd": os.getcwd(), |
| "started_utc": ts, |
| }, |
| indent=2, |
| ) |
| ) |
| return run_dir |
|
|