Files
iomgaa 6bdb802f01 chore: track claude skills, tools, templates, reference code and research-wiki
- Add all claude skills (brainstorming, commit, debugging, TDD, etc.)
- Add claude hooks (pre-commit-guard, post-edit-quality)
- Add research templates (experiment plan, research brief, etc.)
- Add claude tools (arxiv/semantic_scholar/openalex fetch, wiki, exa)
- Add TRM4 reference implementation as algorithm fidelity baseline
- Add research-wiki content (plans, index, graph, query_pack)
- Update .gitignore to exclude .graphify_version runtime state
2026-07-06 20:59:03 -04:00

165 lines
5.3 KiB
Python

"""
日志系统模块
============
提供双通道日志(system.log 文本 + metrics.json 结构化),
以及全局便捷函数 log_msg / log_json / ensure / log_exception。
使用方式::
from utils.logger_system import log_msg, log_json, ensure, log_exception
log_msg("INFO", "模型加载完成", model="bert-base")
log_json("train_loss", {"epoch": 1, "loss": 0.35})
ensure(tensor.shape[0] > 0, "batch 不能为空")
"""
from __future__ import annotations
import json
import logging
import os
import threading
import traceback
from datetime import datetime
from pathlib import Path
from typing import Any, Optional
# ---------------------------------------------------------------------------
# 日志目录
# ---------------------------------------------------------------------------
_LOG_DIR = Path(os.getenv("LOG_DIR", "logs"))
_LOG_DIR.mkdir(parents=True, exist_ok=True)
_SYSTEM_LOG = _LOG_DIR / "system.log"
_METRICS_LOG = _LOG_DIR / "metrics.json"
# ---------------------------------------------------------------------------
# LoggerSystem
# ---------------------------------------------------------------------------
class LoggerSystem:
"""双通道日志系统。
通道 1: system.log — 文本格式,记录所有级别的运行日志。
通道 2: metrics.json — JSON Lines 格式,记录结构化指标数据。
Attributes:
_logger: 标准库 Logger 实例,输出到 system.log。
_metrics_path: metrics.json 文件路径。
"""
_instance: Optional["LoggerSystem"] = None
_lock: threading.Lock = threading.Lock()
def __init__(self, log_dir: Optional[str] = None) -> None:
"""初始化日志系统。
参数:
log_dir: 日志输出目录,默认为 'logs/'。
"""
log_path = Path(log_dir) if log_dir else _LOG_DIR
log_path.mkdir(parents=True, exist_ok=True)
self._metrics_path = log_path / "metrics.json"
# 文本日志
self._logger = logging.getLogger("video_tree_trm")
if not self._logger.handlers:
self._logger.setLevel(logging.DEBUG)
fh = logging.FileHandler(str(log_path / "system.log"), encoding="utf-8")
fh.setLevel(logging.DEBUG)
fmt = logging.Formatter(
"%(asctime)s | %(levelname)-7s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
fh.setFormatter(fmt)
self._logger.addHandler(fh)
@classmethod
def get(cls) -> "LoggerSystem":
"""获取全局单例(线程安全,双重检查锁定)。"""
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = cls()
return cls._instance
# ---- 文本日志 ----
def msg(self, level: str, message: str, **kwargs: Any) -> None:
"""写入文本日志。
参数:
level: 日志级别,如 "INFO", "ERROR", "DEBUG", "WARNING"。
message: 日志消息。
**kwargs: 附加键值对,追加到消息末尾。
"""
extra = " | ".join(f"{k}={v}" for k, v in kwargs.items()) if kwargs else ""
text = f"{message} | {extra}" if extra else message
log_fn = getattr(self._logger, level.lower(), self._logger.info)
log_fn(text)
# ---- 结构化指标 ----
def json(self, tag: str, data: dict[str, Any]) -> None:
"""写入结构化 JSON 指标。
参数:
tag: 指标标签,如 "train_loss"。
data: 指标数据字典。
"""
record = {"ts": datetime.now().isoformat(), "tag": tag, **data}
with open(self._metrics_path, "a", encoding="utf-8") as f:
f.write(json.dumps(record, ensure_ascii=False) + "\n")
# ---- 断言 ----
@staticmethod
def ensure(condition: bool, message: str) -> None:
"""运行时断言,失败时抛出 ValueError。
参数:
condition: 断言条件。
message: 失败时的错误消息。
"""
if not condition:
raise ValueError(message)
# ---- 异常记录 ----
def exception(self, message: str, exc: BaseException) -> None:
"""记录异常信息到日志。
参数:
message: 上下文描述。
exc: 异常实例。
"""
tb = traceback.format_exception(type(exc), exc, exc.__traceback__)
self._logger.error(f"{message} | {''.join(tb)}")
# ---------------------------------------------------------------------------
# 全局便捷函数
# ---------------------------------------------------------------------------
def log_msg(level: str, message: str, **kwargs: Any) -> None:
"""写入文本日志(全局便捷函数)。"""
LoggerSystem.get().msg(level, message, **kwargs)
def log_json(tag: str, data: dict[str, Any]) -> None:
"""写入结构化 JSON 指标(全局便捷函数)。"""
LoggerSystem.get().json(tag, data)
def ensure(condition: bool, message: str) -> None:
"""运行时断言(全局便捷函数)。"""
LoggerSystem.ensure(condition, message)
def log_exception(message: str, exc: BaseException) -> None:
"""记录异常(全局便捷函数)。"""
LoggerSystem.get().exception(message, exc)