logging.basicConfig(level=logging.INFO)配置失效

在进行UI自动化时遇到logging.basicConfig(level=logging.INFO)配置无效的问题,导致控制台未输出日志。通过分析发现,可能因在配置前已存在根logger的处理程序。解决方法是在配置前清空root handlers,确保日志配置生效。执行此操作后,日志正常输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:
在进行UI自动化时,想要输出每次查找元素的日志,引入logging模块,并设置logging.INFO,但是实际上运行正常,但控制台并没有输出日志:

class BasePage:
    _driver = None
    _params={}
    _base_url = ""
    logging.basicConfig(level=logging.INFO)

    def __init__(self,driver:WebDriver = None):
        if driver is None:

            # 和瀏覽器打開的調試端口進行通信,瀏覽器要使用 --remote-debugging-port=9222 開啟調試
            chrome_options = Options()
            chrome_options.debugger_address = "127.0.0.1:9222"
            self._driver = webdriver.Chrome(options=chrome_options)

            self._driver.maximize_window()
            self._driver.implicitly_wait(3)
        else:
            self._driver = driver

        if self._base_url != "":
            self._driver.get(self._base_url)

    @handlie_blacklist
    def find(self,by,locator):
        # 调用find函数即打印日志
        logging.info(f"find:{locator}")
        if by == None:
            result=self._driver.find_element(*locator)
        else:
            result = self._driver.find_element(by,locator)
        return result

解决思路:
反复检查这段代码无误后,只能深入 logging.basicConfig 函数看看这个 python 自带的日志配置函数究竟做了什么(截取一部分)

def basicConfig(**kwargs):
    _acquireLock()
    try:
        if len(root.handlers) == 0:
            handlers = kwargs.pop("handlers", None)
            if handlers is None:
                if "stream" in kwargs and "filename" in kwargs:
                    raise ValueError("'stream' and 'filename' should not be "
                                     "specified together")
            else:
                if "stream" in kwargs or "filename" in kwargs:
                    raise ValueError("'stream' or 'filename' should not be "
                                     "specified together with 'handlers'")
            if handlers is None:
                filename = kwargs.pop("filename", None)
                mode = kwargs.pop("filemode", 'a')
                if filename:
                    h = FileHandler(filename, mode)
                else:
                    stream = kwargs.pop("stream", None)
                    h = StreamHandler(stream)
                handlers = [h]
            dfs = kwargs.pop("datefmt", None)
            style = kwargs.pop("style", '%')
            if style not in _STYLES:
                raise ValueError('Style must be one of: %s' % ','.join(
                                 _STYLES.keys()))
            fs = kwargs.pop("format", _STYLES[style][1])
            fmt = Formatter(fs, dfs, style)
            for h in handlers:
                if h.formatter is None:
                    h.setFormatter(fmt)
                root.addHandler(h)

分析这段程序,有个if语句判断 root.handlers 为空,则会运行后续代码给将每个 handler 添加到根 logger 的逻辑。推测root.handlers 不为空。
看下这个函数basicConfig()说明部分

"""
    Do basic configuration for the logging system.

    This function does nothing if the root logger already has handlers
    configured. It is a convenience method intended for use by simple scripts
    to do one-shot configuration of the logging package.

    The default behaviour is to create a StreamHandler which writes to
    sys.stderr, set a formatter using the BASIC_FORMAT format string, and
    add the handler to the root logger.

    A number of optional keyword arguments may be specified, which can alter
    the default behaviour.
    """

渣道翻译:

对日志系统进行基本配置。
如果根日志记录器已经有处理程序,则此函数不执行任何操作配置。它是一种便于简单脚本使用的方法对日志包进行一次性配置。
默认行为是创建一个写入的StreamHandler。使用BASIC_FORMAT格式字符串设置格式化程序,以及将处理程序添加到根日志记录器。
可以指定许多可选关键字参数,这些参数可以更改默认的行为。

即在图1代码调用logging.basicConfig前已经调用了一次basicConfig,导致设置失效,其实还是使用系统默认的日志文件,根记录器始终默认为警告级别
打个断点验证下:
在这里插入图片描述
说明:
在调用logging.basicConfig前打印了root处理器的handles是有两个的,但是进一步去追踪是哪个地方先行调用了basicConfig,目前我还没有头绪,若有方法,望告知。

解决:
logging.basicConfig前清理已有 handlers

root_logger = logging.getLogger()
    for h in root_logger.handlers[:]:
        root_logger.removeHandler(h)
    logging.basicConfig(level=logging.INFO)

注意:不要写成for h in root_logger.handlers:

再次执行代码,控制台就正常输出日志了,完事收工!
在这里插入图片描述
参考帖子:
追踪方法很好:python logging 模块配置咋不起作用了?
其他logging相关知识:
python中logging日志模块详解
Python日志输出——logging模块
干货:
Python中内置的日志模块logging用法详解
python logging 替代print 输出内容到控制台和重定向到文件

import os import sys import json import logging import time from apscheduler.schedulers.background import BackgroundScheduler from multiprocessing import cpu_count, Pool BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, BASE_DIR) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "teachingReform.settings") import django django.setup() from data_platform_integration.tasks import fetch_and_save_data, init_global_token, load_config logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s', handlers=[ logging.FileHandler("logs/sync.log"), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) # def start_sync_process(table_info): # api_url = table_info.get('api_url') # model_class_name = table_info.get('model_class') # if not api_url or not model_class_name: # logger.warning(f"[-] 配置不完整,跳过表: {table_info}") # return # logger.info(f"[+] 开始处理表: {model_class_name}, API URL: {api_url}") # fetch_and_save_data(api_url, model_class_name, table_info) # logger.info(f"[+] 表 {model_class_name} 处理完成。") def start_sync_process(table_info): # 子进程中重新 setup Django 环境 import os import django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "teachingReform.settings") django.setup() api_url = table_info.get('api_url') model_class_name = table_info.get('model_class') if not api_url or not model_class_name: logger.warning(f"[-] 配置不完整,跳过表: {table_info}") return logger.info(f"[+] 开始处理表: {model_class_name}, API URL: {api_url}") fetch_and_save_data(api_url, model_class_name, table_info) logger.info(f"[+] 表 {model_class_name} 处理完成。") def run_once_immediately(): config = load_config() logger.info("[+] 开始立即执行一次任务...") # 初始化全局 token cached_token = '' cached_expires_at = 0.0 init_global_token(cached_token, cached_expires_at) # 获取所有 enabled 表 tables = config.get('tables', []) filtered_tables = [t for t in tables if t.get('enabled', True)] # 按 last_page 降序排序,页数越大的排前面 sorted_tables = sorted( filtered_tables, key=lambda x: x.get("last_page", 1), reverse=True ) MAX_CONCURRENT_WORKERS = 3 # 固定最大并发数 max_workers = min(MAX_CONCURRENT_WORKERS, cpu_count(), len(sorted_tables)) logger.info(f"[+] 使用 {max_workers} 个工作进程来处理 {len(sorted_tables)} 个表。") with Pool(processes=max_workers) as pool: pool.map(start_sync_process, sorted_tables) # 使用排好序的列表 logger.info("[+] 立即执行任务已完成。") def main(): # 启动定时任务 config = load_config() scheduler = BackgroundScheduler() # 初始化全局 token cached_token = '' cached_expires_at = 0.0 init_global_token(cached_token, cached_expires_at) # 获取所有 enabled 表 tables = config.get('tables', []) filtered_tables = [t for t in tables if t.get('enabled', True)] # 按 last_page 降序排序,页数越大的排前面 sorted_tables = sorted( filtered_tables, key=lambda x: x.get("last_page", 1), reverse=True ) for table_info in sorted_tables: # ✅ 使用排序后的表列表 api_url = table_info.get('api_url') model_class = table_info.get('model_class') if not api_url or not model_class: logger.warning(f"[-] 配置不完整,跳过定时任务: {table_info}") continue job_id = f"sync_{model_class}" if scheduler.get_job(job_id): logger.warning(f"[!] 已存在任务 {job_id},跳过添加。") else: scheduler.add_job( fetch_and_save_data, 'interval', hours=48, args=[api_url, model_class, table_info], id=job_id, misfire_grace_time=30, coalesce=True, max_instances=1 ) logger.info(f"[+] 定时任务 {job_id} 已添加。") scheduler.start() logger.info("[+] 定时任务已启动,按 Ctrl+C 停止程序...") # 确保立即执行任务被调用 run_once_immediately() try: while True: time.sleep(1) except KeyboardInterrupt: scheduler.shutdown() logger.info("[-] 程序已终止。") if __name__ == "__main__": main()
06-13
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值