| """ |
| 日志模块 - 使用环境变量配置 |
| """ |
|
|
| import os |
| import sys |
| import threading |
| from datetime import datetime |
|
|
| |
| LOG_LEVELS = {"debug": 0, "info": 1, "warning": 2, "error": 3, "critical": 4} |
|
|
| |
| _file_lock = threading.Lock() |
|
|
| |
| _file_writing_disabled = False |
| _disable_reason = None |
|
|
|
|
| def _get_current_log_level(): |
| """获取当前日志级别""" |
| level = os.getenv("LOG_LEVEL", "info").lower() |
| return LOG_LEVELS.get(level, LOG_LEVELS["info"]) |
|
|
|
|
| def _get_log_file_path(): |
| """获取日志文件路径""" |
| return os.getenv("LOG_FILE", "log.txt") |
|
|
|
|
| def _clear_log_file(): |
| """清空日志文件(在启动时调用)""" |
| global _file_writing_disabled, _disable_reason |
|
|
| try: |
| log_file = _get_log_file_path() |
| with _file_lock: |
| with open(log_file, "w", encoding="utf-8") as f: |
| f.write("") |
| except (PermissionError, OSError, IOError) as e: |
| |
| _file_writing_disabled = True |
| _disable_reason = str(e) |
| print( |
| f"Warning: File system appears to be read-only or permission denied. " |
| f"Disabling log file writing: {e}", |
| file=sys.stderr, |
| ) |
| print("Log messages will continue to display in console only.", file=sys.stderr) |
| except Exception as e: |
| |
| print(f"Warning: Failed to clear log file: {e}", file=sys.stderr) |
|
|
|
|
| def _write_to_file(message: str): |
| """线程安全地写入日志文件""" |
| global _file_writing_disabled, _disable_reason |
|
|
| |
| if _file_writing_disabled: |
| return |
|
|
| try: |
| log_file = _get_log_file_path() |
| with _file_lock: |
| with open(log_file, "a", encoding="utf-8") as f: |
| f.write(message + "\n") |
| f.flush() |
| except (PermissionError, OSError, IOError) as e: |
| |
| _file_writing_disabled = True |
| _disable_reason = str(e) |
| print( |
| f"Warning: File system appears to be read-only or permission denied. " |
| f"Disabling log file writing: {e}", |
| file=sys.stderr, |
| ) |
| print("Log messages will continue to display in console only.", file=sys.stderr) |
| except Exception as e: |
| |
| print(f"Warning: Failed to write to log file: {e}", file=sys.stderr) |
|
|
|
|
| def _log(level: str, message: str): |
| """ |
| 内部日志函数 |
| """ |
| level = level.lower() |
| if level not in LOG_LEVELS: |
| print(f"Warning: Unknown log level '{level}'", file=sys.stderr) |
| return |
|
|
| |
| current_level = _get_current_log_level() |
| if LOG_LEVELS[level] < current_level: |
| return |
|
|
| |
| |
| |
|
|
| |
| timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
| entry = f"[{timestamp}] [{level.upper()}] {message}" |
|
|
| |
| if level in ("error", "critical"): |
| print(entry, file=sys.stderr) |
| else: |
| print(entry) |
|
|
| |
| _write_to_file(entry) |
|
|
|
|
| def set_log_level(level: str): |
| """设置日志级别提示""" |
| level = level.lower() |
| if level not in LOG_LEVELS: |
| print(f"Warning: Unknown log level '{level}'. Valid levels: {', '.join(LOG_LEVELS.keys())}") |
| return False |
|
|
| print(f"Note: To set log level '{level}', please set LOG_LEVEL environment variable") |
| return True |
|
|
|
|
| class Logger: |
| """支持 log('info', 'msg') 和 log.info('msg') 两种调用方式""" |
|
|
| def __call__(self, level: str, message: str): |
| """支持 log('info', 'message') 调用方式""" |
| _log(level, message) |
|
|
| def debug(self, message: str): |
| """记录调试信息""" |
| _log("debug", message) |
|
|
| def info(self, message: str): |
| """记录一般信息""" |
| _log("info", message) |
|
|
| def warning(self, message: str): |
| """记录警告信息""" |
| _log("warning", message) |
|
|
| def error(self, message: str): |
| """记录错误信息""" |
| _log("error", message) |
|
|
| def critical(self, message: str): |
| """记录严重错误信息""" |
| _log("critical", message) |
|
|
| def get_current_level(self) -> str: |
| """获取当前日志级别名称""" |
| current_level = _get_current_log_level() |
| for name, value in LOG_LEVELS.items(): |
| if value == current_level: |
| return name |
| return "info" |
|
|
| def get_log_file(self) -> str: |
| """获取当前日志文件路径""" |
| return _get_log_file_path() |
|
|
|
|
| |
| log = Logger() |
|
|
| |
| __all__ = ["log", "set_log_level", "LOG_LEVELS"] |
|
|
| |
| _clear_log_file() |
|
|
| |
| |
| |
|
|