Python自定义日志器:如何将调用脚本名加入日志文件名
问题解决:日志文件名动态添加调用脚本名称
问题背景
已实现日志文件名追加日期时间(示例:2023-12-11_10.44.33. - Test Log.txt),但尝试用%(module)s添加调用脚本名称时,无法被正确解析替换,文件名直接显示%(module)s字符串。需要让文件名变为2023-12-11_10.44.33. - level2 - Test Log.txt的形式。
核心原因
%(module)s是日志内容的格式变量,仅在日志条目格式化时生效;而FileHandler初始化时就固定了文件名,无法自动解析这类日志格式变量,必须主动获取调用脚本的名称并手动拼接进文件名。
解决方案
- 获取调用脚本名称:通过
sys.argv[0]拿到调用脚本的路径,提取文件名并去掉后缀; - 修改文件名生成函数:给
timeStamped新增模块名参数,将其整合到文件名模板中; - 传入参数生成文件名:创建
FileHandler时,把获取到的脚本名称传入timeStamped函数,生成带模块名的文件名。
修改后的完整代码
import os import sys import datetime import logging from collections import UserList # 切换到日志输出目录 os.chdir('C:\\Git_Repo\\pyautotest\\AutoTest\\OutputLogs') def timeStamped(fname, module_name, fmt='%Y-%m-%d_%H.%M.%S. - {module} - {fname}'): return datetime.datetime.now().strftime(fmt).format(module=module_name, fname=fname) # 日志级别说明 # DEBUG: 调试细节信息,仅排查问题时有用 # INFO: 确认功能正常运行 # WARNING: 异常情况提示,软件仍可正常工作 # ERROR: 功能执行失败,软件部分功能受影响 # CRITICAL: 严重错误,程序可能无法继续运行 # 获取调用脚本名称(去掉后缀) caller_module = os.path.splitext(os.path.basename(sys.argv[0]))[0] stream_handler = logging.StreamHandler() # 传入调用模块名称生成目标文件名 file_handler = logging.FileHandler(timeStamped('Test Log.txt', caller_module)) PrintLog = logging.getLogger(__name__) PrintLog.setLevel(logging.DEBUG) PrintLog.addHandler(stream_handler) PrintLog.addHandler(file_handler) class MyFormatter(logging.Formatter): # 不同日志级别的自定义格式 debug_fmt = "%(levelname)s: %(asctime)s: %(pathname)s: Line %(lineno)s: %(message)s" info_fmt = "%(message)s" error_fmt = "%(levelname)s: %(asctime)s: %(pathname)s: Line %(lineno)s: ERROR - %(message)s" def __init__(self): super().__init__(fmt="%(levelno)d: %(module)s: %(msg)s", datefmt='%Y-%m-%d_%H.%M', style='%') def format(self, record): # 保存原始格式 format_orig = self._style._fmt # 根据日志级别切换格式 if record.levelno == logging.DEBUG: self._style._fmt = MyFormatter.debug_fmt elif record.levelno == logging.INFO: self._style._fmt = MyFormatter.info_fmt elif record.levelno == logging.ERROR: self._style._fmt = MyFormatter.error_fmt # 执行格式化 result = logging.Formatter.format(self, record) # 恢复原始格式 self._style._fmt = format_orig return result fmt = MyFormatter() PrintLog.propagate = False stream = logging.Formatter('%(message)s') stream_handler.setFormatter(stream) file_handler.setFormatter(fmt) class ListFormatter(UserList): def __init__(self, iterable): super().__init__(str(item) for item in iterable) def __setitem__(self, index, item): self.data[index] = str(item),'\n' def insert(self, index, item): self.data.insert(index, str(item),'\n') def append(self, item): self.data.append(str(item)) def extend(self, other): if isinstance(other, type(self)): self.data.extend(other) else: self.data.extend(str(item) for item in other) today = datetime.datetime.now() date_time = today.strftime("%H:%M:%S, on %d/%m/%Y") class PrintAppended(): def __init__(self): PrintLog.info(''.join(map(str, ListFormatter([])))) PrintLog.info('\n') PrintLog.info('Testing concluded at ' + date_time +'.')
额外说明
- 原代码中
PrintLog.info(format(''.join(map(str, ListFormatter))))存在实例化参数缺失问题,已修正为PrintLog.info(''.join(map(str, ListFormatter([])))); - 若需获取更上层调用栈的模块名(比如日志模块被多层导入时),可改用
sys._getframe(1).f_globals['__name__']替代sys.argv[0]的方式。
内容的提问来源于stack exchange,提问作者Chris Trace
相关产品推荐
相关产品推荐

