通过日志模块实现Python进度条

19 投票
6 回答
36684 浏览
提问于 2025-04-17 16:00

我看到过很多关于在Python中实现进度条的不同方法,但那些简单的标准输出解决方案在我的项目中不太管用。我有多个类,并且使用“logging”模块来输出信息到标准输出。我有一个函数,想在一行上显示进度条,每次都刷新缓冲区。

下面是一个简单的进度条示例:

for i in range(100):
    time.sleep(1)
    sys.stdout.write("\r%d%%" %i)
    sys.stdout.flush()

当我尝试通过标准输出写入并刷新缓冲区时,要么缓冲区没有被刷新,要么进度条没有任何变化。我希望能避免使用线程或其他复杂的过程来实现这个功能。有没有人有更好的方法来做到这一点?

6 个回答

6

你可以使用 tqdm 进度条,并通过 自定义的日志处理器 来实现,具体方法可以参考这里的说明:

import logging
import time
import colorlog
from tqdm import tqdm

class TqdmHandler(logging.StreamHandler):
    def __init__(self):
        logging.StreamHandler.__init__(self)

    def emit(self, record):
        msg = self.format(record)
        tqdm.write(msg)

if __name__ == "__main__":
    for x in tqdm(range(100)):
        logger = colorlog.getLogger("MYAPP")
        logger.setLevel(logging.DEBUG)
        handler = TqdmHandler()
        handler.setFormatter(colorlog.ColoredFormatter(
            '%(log_color)s%(name)s | %(asctime)s | %(levelname)s | %(message)s',
            datefmt='%Y-%d-%d %H:%M:%S',
            log_colors={
                'DEBUG': 'cyan',
                'INFO': 'white',
                'SUCCESS:': 'green',
                'WARNING': 'yellow',
                'ERROR': 'red',
                'CRITICAL': 'red,bg_white'},))

        logger.addHandler(handler)
        logger.debug("Inside subtask: "+str(x))
        time.sleep(.5)
17

我这样解决的:

import logging
import time
from tqdm import tqdm
import io

class TqdmToLogger(io.StringIO):
    """
        Output stream for TQDM which will output to logger module instead of
        the StdOut.
    """
    logger = None
    level = None
    buf = ''
    def __init__(self,logger,level=None):
        super(TqdmToLogger, self).__init__()
        self.logger = logger
        self.level = level or logging.INFO
    def write(self,buf):
        self.buf = buf.strip('\r\n\t ')
    def flush(self):
        self.logger.log(self.level, self.buf)

if __name__ == "__main__":
    logging.basicConfig(format='%(asctime)s [%(levelname)-8s] %(message)s')
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    tqdm_out = TqdmToLogger(logger,level=logging.INFO)
    for x in tqdm(range(100),file=tqdm_out,mininterval=30,):
        time.sleep(.5)

输出结果

2016-12-19 15:35:06 [INFO    ] 16%|#####9                                | 768/4928 [07:04<40:50,  1.70it/s]
2016-12-19 15:36:07 [INFO    ] 18%|######6                               | 865/4928 [08:04<40:34,  1.67it/s]
30

我找不到一个好的解决办法,所以我写了一个叫做 enlighten 进度条 的工具来处理这个问题。简单来说,它会改变终端的滚动区域,这样在显示进度条的时候,日志信息就会显示在进度条的上方,而不是每次想要输出信息的时候都要重新绘制进度条。这样你就可以随意在终端上写信息,而不需要去修改 日志打印 等等。

import logging
import time
import enlighten

# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger()

# Setup progress bar
manager = enlighten.get_manager()
pbar = manager.counter(total=100, desc='Ticks', unit='ticks')

for i in range(1, 101):
    logger.info("Processing step %s" % i)
    time.sleep(.2)
    pbar.update()

撰写回答