refactor: 优化日志系统及自动化备份机制

- 替换 loguru 为原生 logging 库(与pytest兼容性更好)。
- 更新 pytest.ini 统一配置日志格式和基础命令。
- 优化 main.py 增加测试后的日志自动备份与定期清理功能。
- 新增 settings.py 实现配置解耦
- 更新 .gitignore
This commit is contained in:
2026-01-19 17:55:50 +08:00
parent 9355a576a9
commit a53a26766d
9 changed files with 263 additions and 146 deletions

View File

@@ -9,61 +9,21 @@
@date: 2026/1/15 11:30
@desc:
"""
import sys
import time
import functools
from pathlib import Path
import inspect
from loguru import logger
import logging
from core.settings import LOG_DIR
# 1. 确定日志存储路径
LOG_DIR = Path(__file__).parent.parent / "logs"
LOG_DIR.mkdir(exist_ok=True)
LOG_DIR.mkdir(parents=True, exist_ok=True)
# 2. 统一定义日志格式 (美化版)
# <green> 等标签是控制台颜色,文件日志中会自动剥离颜色代码
LOG_FORMAT = (
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | "
"<magenta>{extra[source]: <8}</magenta> | "
"<cyan>{module}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
"<level>{message}</level>"
)
logger = logging.getLogger(__name__)
def setup_logger():
"""
只需在项目入口调用一次。
如果是简单的自动化脚本,甚至可以直接在模块内执行。
"""
# 移除 Loguru 默认的控制台处理器(避免重复打印)
logger.remove()
# 添加自定义控制台输出
logger.add(
sys.stdout,
format=LOG_FORMAT,
level="INFO",
colorize=True,
# 默认给一个 'Global' 的 device 标签
filter=lambda record: record["extra"].setdefault("source", "System")
)
# 添加按天滚动的日志文件
logger.add(
str(LOG_DIR / "appium_{time:YYYY-MM-DD}.log"),
format=LOG_FORMAT,
level="DEBUG",
rotation="00:00", # 每天午夜滚动
retention="10 days", # 保留最近10天
compression="zip", # 旧日志自动压缩
encoding="utf-8",
enqueue=True # 开启队列模式,确保多线程下日志不串行
)
# --- 核心特性 1装饰器集成 ---
def trace_step(step_desc="", source: str = 'task'):
# --- 核心特性:装饰器集成 ---
def trace_step(step_desc="", source='wrapper'):
"""
通用执行追踪装饰器:
1. 智能识别并过滤 self/cls 参数
@@ -74,17 +34,14 @@ def trace_step(step_desc="", source: str = 'task'):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# --- 智能参数解析 ---
# 获取函数的签名
# 获取对应的 logger
_logger = logging.getLogger(source)
# _logger = logging.getLogger(f"{source}.{func.__name__}")
# 参数解析 (跳过 self/cls)
sig = inspect.signature(func)
params = list(sig.parameters.values())
# 检查第一个参数名是否为 'self' 或 'cls'
# 这样既兼容了 PageObject 的实例方法,也兼容了纯函数
if params and params[0].name in ('self', 'cls'):
display_args = args[1:]
else:
display_args = args
display_args = args[1:] if params and params[0].name in ('self', 'cls') else args
# 格式化参数显示,方便阅读
args_repr = [repr(a) for a in display_args]
@@ -93,32 +50,21 @@ def trace_step(step_desc="", source: str = 'task'):
func_name = f"{func.__module__}.{func.__name__}"
# 使用 bind 临时改变这一步的 source 标签
_logger = logger.bind(source=source)
# 使用关联的上下文 logger
# logger.info(f"🚀 [步骤开始] {step_desc} | 执行方法: {func_name} | 参数: {display_args} {kwargs}")
_logger.info(f"🚀 [步骤开始] {step_desc} | 方法: {func_name}({all_params})")
_logger.info(f"[步骤开始] | {step_desc} | 方法: {func_name}({all_params})")
start_t = time.perf_counter()
try:
result = func(*args, **kwargs)
duration = time.perf_counter() - start_t
_logger.success(f"[步骤成功] {step_desc} | 耗时: {duration:.2f}s | 返回: {result!r}")
_logger.info(f"[步骤成功] {step_desc} | 耗时: {duration:.2f}s | 返回: {result!r}")
return result
except Exception as e:
duration = time.perf_counter() - start_t
_logger.error(
f"[步骤失败] {step_desc} | 耗时: {duration:.2f}s | 异常: {type(e).__name__}: {e}")
# logging.exception 关键点:它会自动把详细的堆栈信息写入日志文件
_logger.error(f"[步骤失败] {step_desc}| 耗时: {duration:.2f}s|异常: {type(e).__name__}")
# f"[步骤失败] {step_desc} | 耗时: {duration:.2f}s | 异常: {type(e).__name__}: {e}")
raise e
return wrapper
return decorator
# 初始化
setup_logger()
# 导出供外部使用
__all__ = ["logger", "trace_step"]