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

Python logging:如何避免未打印日志时执行其中的函数?

问题描述

我有一些仅用于调试的耗时较长的函数,当前即使在更高日志级别运行时,这些函数仍会执行,但我希望避免这种情况。我知道可以用if logger.getEffectiveLevel()来规避,但有没有更优雅的实现方式?

示例代码

import logging
import time

def waitsec():
    time.sleep(5)

logger = logging.getLogger("Logger")
logger.setLevel(logging.ERROR)

logger.error(f'Before call waitsec')
logger.debug(f'This is not printed, but still waits 5 seconds {waitsec()}') # 希望完全跳过这行的执行
logger.error(f'After call waitsec')

当前输出

Before call waitsec
After call waitsec

优雅的解决方案

1. 用isEnabledFor做前置判断

这是贴合日志模块设计的标准写法,语义比直接判断级别更清晰:

import logging
import time

def waitsec():
    time.sleep(5)

logger = logging.getLogger("Logger")
logger.setLevel(logging.ERROR)

logger.error(f'Before call waitsec')
if logger.isEnabledFor(logging.DEBUG):
    logger.debug(f'This will only run when DEBUG is enabled {waitsec()}')
logger.error(f'After call waitsec')

只有当DEBUG级别启用时,才会执行waitsec()并输出日志,彻底避免不必要的耗时。

2. 改用日志原生的延迟求值格式

原问题的核心是f-string会在调用logger.debug前就完成所有表达式计算,换成%格式化的日志写法,就能让参数里的函数调用延迟到日志实际输出时才执行:

# 用%s占位符替代f-string,此时waitsec()不会立即执行
logger.debug('This will only run when DEBUG is enabled %s', waitsec())

只有当DEBUG级别生效时,waitsec()才会被调用。

3. 用装饰器批量处理调试函数

如果有大量这类耗时调试函数,写个装饰器统一控制执行逻辑:

import logging
import time
from functools import wraps

def debug_only(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        logger = logging.getLogger(func.__module__)
        if logger.isEnabledFor(logging.DEBUG):
            return func(*args, **kwargs)
        return None
    return wrapper

@debug_only
def waitsec():
    time.sleep(5)
    return "waited 5s"

logger = logging.getLogger("Logger")
logger.setLevel(logging.ERROR)

logger.error(f'Before call waitsec')
logger.debug(f'This is not printed, and waitsec is not called {waitsec()}')
logger.error(f'After call waitsec')

被装饰的函数在非DEBUG级别下会直接返回None,不会执行耗时逻辑。

4. 自定义延迟日志适配器(进阶)

如果需要全局统一处理延迟求值,可以封装一个日志适配器:

import logging

class LazyLoggerAdapter(logging.LoggerAdapter):
    def debug(self, msg, *args, **kwargs):
        if self.isEnabledFor(logging.DEBUG):
            # 自动执行可调用参数
            args = [arg() if callable(arg) else arg for arg in args]
            super().debug(msg, *args, **kwargs)

logger = LazyLoggerAdapter(logging.getLogger("Logger"), {})
logger.setLevel(logging.ERROR)

def waitsec():
    time.sleep(5)
    return "waited 5s"

logger.error(f'Before call waitsec')
logger.debug('This will only run when DEBUG is enabled %s', waitsec) # 直接传函数对象
logger.error(f'After call waitsec')

适配器会在DEBUG级别生效时才调用传入的函数对象,无需手动判断。


内容的提问来源于stack exchange,提问作者Sombrero

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 10:30:33