2026-06-06 09:16:49 +08:00
|
|
|
|
import os
|
2026-06-11 08:04:08 +08:00
|
|
|
|
from pathlib import Path
|
2026-06-09 10:57:28 +08:00
|
|
|
|
from datetime import datetime, timedelta
|
2026-06-06 09:16:49 +08:00
|
|
|
|
import logging
|
|
|
|
|
|
from logging.handlers import RotatingFileHandler
|
2026-06-09 10:57:28 +08:00
|
|
|
|
import inspect
|
2026-06-06 09:16:49 +08:00
|
|
|
|
from PubLibrary.InifileHelper import IniRead
|
|
|
|
|
|
|
2026-06-11 08:04:08 +08:00
|
|
|
|
|
2026-06-09 10:57:28 +08:00
|
|
|
|
# 全局配置
|
2026-06-06 09:16:49 +08:00
|
|
|
|
console_output = IniRead('system', 'console_output', '1')
|
|
|
|
|
|
log_level = IniRead('system', 'algo_log_level', 'INFO')
|
|
|
|
|
|
log_once_cache = set()
|
2026-06-06 16:04:33 +08:00
|
|
|
|
logger_cache = {}
|
2026-06-09 10:57:28 +08:00
|
|
|
|
LOG_RETENTION_DAYS = 3
|
2026-06-11 08:04:08 +08:00
|
|
|
|
|
|
|
|
|
|
LOG_PATH_STR = IniRead('system', 'algo_log_path', "d:/Program Files/64chn_Decoder/logs")
|
|
|
|
|
|
LOG_DIR = Path(LOG_PATH_STR)
|
|
|
|
|
|
# 自动补全路径分隔符,创建目录(不存在则新建,避免写日志报错)
|
|
|
|
|
|
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
# 如需字符串格式路径
|
|
|
|
|
|
LOG_DIR_STR = str(LOG_DIR) + "\\"
|
2026-06-09 10:57:28 +08:00
|
|
|
|
LOG_FILE_PREFIX = 'algo_log_'
|
|
|
|
|
|
|
|
|
|
|
|
# 日志格式:时间 - 日志器名 - 级别 - 文件名:行号 - 函数名 - 日志内容
|
|
|
|
|
|
LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
|
|
|
|
DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def clean_old_logs():
|
|
|
|
|
|
"""清理超过指定天数的旧日志文件"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not os.path.exists(LOG_DIR):
|
|
|
|
|
|
return
|
|
|
|
|
|
expire_date = datetime.now() - timedelta(days=LOG_RETENTION_DAYS)
|
|
|
|
|
|
for filename in os.listdir(LOG_DIR):
|
|
|
|
|
|
if not filename.startswith(LOG_FILE_PREFIX) or not filename.endswith('.log'):
|
|
|
|
|
|
continue
|
|
|
|
|
|
date_str = filename[len(LOG_FILE_PREFIX):-4]
|
|
|
|
|
|
try:
|
|
|
|
|
|
file_date = datetime.strptime(date_str, '%Y-%m-%d')
|
|
|
|
|
|
if file_date < expire_date:
|
|
|
|
|
|
file_path = os.path.join(LOG_DIR, filename)
|
|
|
|
|
|
os.remove(file_path)
|
|
|
|
|
|
print(f"清理过期日志: {file_path}")
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
continue
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"清理旧日志异常: {str(e)}")
|
|
|
|
|
|
|
2026-06-06 09:16:49 +08:00
|
|
|
|
|
2026-06-06 16:04:33 +08:00
|
|
|
|
def init_module_logger(logger_name):
|
2026-06-09 10:57:28 +08:00
|
|
|
|
"""初始化日志器 + 清理旧日志"""
|
|
|
|
|
|
os.makedirs(LOG_DIR, exist_ok=True)
|
|
|
|
|
|
clean_old_logs()
|
|
|
|
|
|
|
|
|
|
|
|
current_date = datetime.now().strftime("%Y-%m-%d")
|
|
|
|
|
|
log_file = os.path.join(LOG_DIR, f"{LOG_FILE_PREFIX}{current_date}.log")
|
2026-06-06 09:16:49 +08:00
|
|
|
|
|
2026-06-06 16:04:33 +08:00
|
|
|
|
if logger_name in logger_cache:
|
|
|
|
|
|
return logger_cache[logger_name]
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(logger_name)
|
|
|
|
|
|
logger.setLevel(log_level)
|
2026-06-06 09:16:49 +08:00
|
|
|
|
if logger.handlers:
|
2026-06-06 16:04:33 +08:00
|
|
|
|
logger_cache[logger_name] = logger
|
|
|
|
|
|
return logger
|
2026-06-06 09:16:49 +08:00
|
|
|
|
|
2026-06-09 10:57:28 +08:00
|
|
|
|
# 文件输出处理器
|
2026-06-06 09:16:49 +08:00
|
|
|
|
file_handler = RotatingFileHandler(
|
|
|
|
|
|
log_file,
|
2026-06-09 10:57:28 +08:00
|
|
|
|
maxBytes=10 * 1024 * 1024,
|
2026-06-06 09:16:49 +08:00
|
|
|
|
backupCount=10,
|
|
|
|
|
|
encoding='utf-8'
|
|
|
|
|
|
)
|
2026-06-09 10:57:28 +08:00
|
|
|
|
formatter = logging.Formatter(LOG_FORMAT, datefmt=DATE_FORMAT)
|
2026-06-06 09:16:49 +08:00
|
|
|
|
file_handler.setFormatter(formatter)
|
|
|
|
|
|
logger.addHandler(file_handler)
|
2026-06-06 16:04:33 +08:00
|
|
|
|
|
2026-06-09 10:57:28 +08:00
|
|
|
|
# 控制台输出
|
2026-06-06 09:16:49 +08:00
|
|
|
|
if console_output:
|
|
|
|
|
|
console_handler = logging.StreamHandler()
|
|
|
|
|
|
console_handler.setFormatter(formatter)
|
|
|
|
|
|
logger.addHandler(console_handler)
|
|
|
|
|
|
|
2026-06-06 16:04:33 +08:00
|
|
|
|
logger_cache[logger_name] = logger
|
2026-06-06 09:16:49 +08:00
|
|
|
|
return logger
|
|
|
|
|
|
|
2026-06-06 16:04:33 +08:00
|
|
|
|
|
2026-06-06 09:16:49 +08:00
|
|
|
|
def algo_log(content, level="INFO", record_once=False):
|
2026-06-09 10:57:28 +08:00
|
|
|
|
"""
|
|
|
|
|
|
日志入口函数
|
|
|
|
|
|
自动记录:调用文件名、代码行号、所在函数
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 回溯栈帧,获取真正调用 algo_log 的代码位置
|
|
|
|
|
|
# f_back(1) -> algo_log 自身,f_back(2) -> 业务调用处
|
|
|
|
|
|
frame = inspect.currentframe().f_back.f_back
|
|
|
|
|
|
if not frame:
|
|
|
|
|
|
file_name = "unknown"
|
|
|
|
|
|
else:
|
|
|
|
|
|
file_name = os.path.basename(frame.f_code.co_filename)
|
2026-06-06 16:04:33 +08:00
|
|
|
|
|
|
|
|
|
|
logger = init_module_logger(file_name)
|
|
|
|
|
|
|
2026-06-09 10:57:28 +08:00
|
|
|
|
# 单次日志去重
|
2026-06-06 09:16:49 +08:00
|
|
|
|
if record_once:
|
|
|
|
|
|
log_key = f"{level.upper()}_{content}"
|
|
|
|
|
|
if log_key in log_once_cache:
|
2026-06-06 16:04:33 +08:00
|
|
|
|
return
|
|
|
|
|
|
log_once_cache.add(log_key)
|
|
|
|
|
|
|
2026-06-09 10:57:28 +08:00
|
|
|
|
# 日志级别分发
|
2026-06-06 09:16:49 +08:00
|
|
|
|
level_upper = level.upper()
|
2026-06-09 10:57:28 +08:00
|
|
|
|
log_map = {
|
|
|
|
|
|
"DEBUG": logger.debug,
|
|
|
|
|
|
"WARNING": logger.warning,
|
|
|
|
|
|
"ERROR": logger.error,
|
|
|
|
|
|
"FATAL": logger.fatal,
|
|
|
|
|
|
"INFO": logger.info
|
|
|
|
|
|
}
|
|
|
|
|
|
log_func = log_map.get(level_upper, logger.info)
|
|
|
|
|
|
log_func(content)
|