|
|
""" |
|
|
日志模块 - 使用环境变量配置 |
|
|
""" |
|
|
|
|
|
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() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|