add configurable log level support and improve logging setup

This commit is contained in:
sam 2025-10-17 18:40:29 +08:00
parent 4b68d84b3c
commit 081b223b08
2 changed files with 34 additions and 21 deletions

View File

@ -553,6 +553,7 @@ class AppConfig:
"""User configurable settings persisted in a simple structure.""" """User configurable settings persisted in a simple structure."""
tushare_token: Optional[str] = None tushare_token: Optional[str] = None
log_level: str = "DEBUG"
rss_sources: Dict[str, object] = field(default_factory=_default_rss_sources) rss_sources: Dict[str, object] = field(default_factory=_default_rss_sources)
decision_method: str = "nash" decision_method: str = "nash"
data_paths: DataPaths = field(default_factory=DataPaths) data_paths: DataPaths = field(default_factory=DataPaths)
@ -625,6 +626,9 @@ def _load_from_file(cfg: AppConfig) -> None:
cfg.force_refresh = bool(payload.get("force_refresh")) cfg.force_refresh = bool(payload.get("force_refresh"))
if "auto_update_data" in payload: if "auto_update_data" in payload:
cfg.auto_update_data = bool(payload.get("auto_update_data")) cfg.auto_update_data = bool(payload.get("auto_update_data"))
log_level_raw = payload.get("log_level")
if isinstance(log_level_raw, str) and log_level_raw.strip():
cfg.log_level = log_level_raw.strip()
if "decision_method" in payload: if "decision_method" in payload:
cfg.decision_method = str(payload.get("decision_method") or cfg.decision_method) cfg.decision_method = str(payload.get("decision_method") or cfg.decision_method)
@ -934,6 +938,7 @@ def save_config(cfg: AppConfig | None = None) -> None:
path = cfg.data_paths.config_file path = cfg.data_paths.config_file
payload = { payload = {
"tushare_token": cfg.tushare_token, "tushare_token": cfg.tushare_token,
"log_level": cfg.log_level,
"force_refresh": cfg.force_refresh, "force_refresh": cfg.force_refresh,
"auto_update_data": cfg.auto_update_data, "auto_update_data": cfg.auto_update_data,
"decision_method": cfg.decision_method, "decision_method": cfg.decision_method,
@ -1022,6 +1027,10 @@ def _load_env_defaults(cfg: AppConfig) -> None:
if token: if token:
cfg.tushare_token = token.strip() cfg.tushare_token = token.strip()
log_level_env = os.getenv("LLM_QUANT_LOG_LEVEL")
if log_level_env:
cfg.log_level = log_level_env.strip()
api_key = os.getenv("LLM_API_KEY") api_key = os.getenv("LLM_API_KEY")
if api_key: if api_key:
sanitized = api_key.strip() sanitized = api_key.strip()

View File

@ -7,7 +7,6 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
import os
import sqlite3 import sqlite3
import sys import sys
from datetime import datetime from datetime import datetime
@ -53,7 +52,7 @@ def _build_formatter() -> logging.Formatter:
def setup_logging( def setup_logging(
*, *,
level: int = logging.INFO, level: Optional[int] = None,
console_level: Optional[int] = None, console_level: Optional[int] = None,
file_level: Optional[int] = None, file_level: Optional[int] = None,
db_level: Optional[int] = None, db_level: Optional[int] = None,
@ -64,19 +63,24 @@ def setup_logging(
if _IS_CONFIGURED: if _IS_CONFIGURED:
return logging.getLogger() return logging.getLogger()
env_level = os.getenv("LLM_QUANT_LOG_LEVEL")
if env_level is None:
level = logging.DEBUG
else:
try:
level = getattr(logging, env_level.upper())
except AttributeError:
logging.getLogger(_LOGGER_NAME).warning(
"非法的日志级别 %s,回退到 DEBUG", env_level
)
level = logging.DEBUG
cfg = get_config() cfg = get_config()
resolved_level = logging.DEBUG
level_name = getattr(cfg, "log_level", None)
if isinstance(level_name, str) and level_name.strip():
normalized_name = level_name.strip()
try:
resolved_level = getattr(logging, normalized_name.upper())
except AttributeError:
try:
resolved_level = int(normalized_name)
except (TypeError, ValueError):
logging.getLogger(_LOGGER_NAME).warning(
"非法的日志级别 %s,回退到 DEBUG", normalized_name
)
resolved_level = logging.DEBUG
elif level is not None:
resolved_level = int(level)
timestamp = datetime.now().strftime("%Y%m%d_%H%M") timestamp = datetime.now().strftime("%Y%m%d_%H%M")
log_dir: Path = cfg.data_paths.root / "logs" log_dir: Path = cfg.data_paths.root / "logs"
log_dir.mkdir(parents=True, exist_ok=True) log_dir.mkdir(parents=True, exist_ok=True)
@ -84,28 +88,27 @@ def setup_logging(
conversation_logfile = log_dir / f"agent_{timestamp}.log" conversation_logfile = log_dir / f"agent_{timestamp}.log"
root = logging.getLogger() root = logging.getLogger()
root.setLevel(level) root.setLevel(resolved_level)
root.handlers.clear() root.handlers.clear()
formatter = _build_formatter() formatter = _build_formatter()
console_handler = logging.StreamHandler(stream=sys.stdout) console_handler = logging.StreamHandler(stream=sys.stdout)
console_handler.setLevel(console_level or level) console_handler.setLevel(console_level if console_level is not None else resolved_level)
console_handler.setFormatter(formatter) console_handler.setFormatter(formatter)
root.addHandler(console_handler) root.addHandler(console_handler)
file_handler = logging.FileHandler(logfile, encoding="utf-8") file_handler = logging.FileHandler(logfile, encoding="utf-8")
file_handler.setLevel(file_level or level) file_handler.setLevel(file_level if file_level is not None else resolved_level)
file_handler.setFormatter(formatter) file_handler.setFormatter(formatter)
root.addHandler(file_handler) root.addHandler(file_handler)
db_handler = DatabaseLogHandler(level=db_level or level) db_handler = DatabaseLogHandler(level=db_level if db_level is not None else resolved_level)
db_handler.setFormatter(formatter) db_handler.setFormatter(formatter)
root.addHandler(db_handler) root.addHandler(db_handler)
_IS_CONFIGURED = True _IS_CONFIGURED = True
cfg = get_config()
root.info( root.info(
"日志系统初始化完成", "日志系统初始化完成",
extra={ extra={
@ -115,15 +118,16 @@ def setup_logging(
"force_refresh": cfg.force_refresh, "force_refresh": cfg.force_refresh,
"data_root": str(cfg.data_paths.root), "data_root": str(cfg.data_paths.root),
"logfile": str(logfile), "logfile": str(logfile),
"log_level": getattr(cfg, "log_level", resolved_level),
}, },
}, },
) )
conversation_logger = logging.getLogger(_CONVERSATION_LOGGER_NAME) conversation_logger = logging.getLogger(_CONVERSATION_LOGGER_NAME)
conversation_logger.setLevel(level) conversation_logger.setLevel(resolved_level)
conversation_logger.handlers.clear() conversation_logger.handlers.clear()
conversation_logger.propagate = False conversation_logger.propagate = False
conv_handler = logging.FileHandler(conversation_logfile, encoding="utf-8") conv_handler = logging.FileHandler(conversation_logfile, encoding="utf-8")
conv_handler.setLevel(level) conv_handler.setLevel(resolved_level)
conv_handler.setFormatter(formatter) conv_handler.setFormatter(formatter)
conversation_logger.addHandler(conv_handler) conversation_logger.addHandler(conv_handler)