You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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初始化时就固定了文件名,无法自动解析这类日志格式变量,必须主动获取调用脚本的名称并手动拼接进文件名。

解决方案

  1. 获取调用脚本名称:通过sys.argv[0]拿到调用脚本的路径,提取文件名并去掉后缀;
  2. 修改文件名生成函数:给timeStamped新增模块名参数,将其整合到文件名模板中;
  3. 传入参数生成文件名:创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:25:00