文章目录

  • 切换系统输出流
  • logging模块输出日志
  • 更实用的方法(一次实现,全局调用)
  • 一般写大型程序的时候,程序运行时间都比较长,将一些必要的输出保存到日志文件中是很有必要的。下面提供两种将输出保存到日志文件的方法,以及一种
    非常实用的全局调用方法。

    切换系统输出流

    第一种方法是将系统输出流切换至文件句柄。

    import os
    import sys
    
    # make a copy of original stdout route
    stdout_backup = sys.stdout
    
    # 日志文件目录
    file_path="message"
    if not os.path.exists(os.path.join(os.getcwd(),file_path)):
    	# os.getcwd()用于获取当前文件目录
        os.mkdir(os.path.join(os.getcwd(),file_path))
    
    log_file = open(os.path.join(file_path,'log.txt'), "w")
    
    # redirect print output to log file
    sys.stdout = log_file # 将系统输出切换至log_file
    
    print ("Now all print info will be written to message.log")
    # any command line that you will execute
    
    log_file.close()
    # restore the output to initial pattern
    sys.stdout = stdout_backup #将系统输出切换回console
    
    print ("Now this will be presented on screen")
    

    这种方式可以将所有print函数打印的信息保存到日志文件中,但是这些信息无法同时显示在console中。

    logging模块输出日志

    为了实现同时输出到console和日志文件,可以使用logging模块输出日志。

    from __future__ import absolute_import
    from __future__ import division
    from __future__ import print_function
    
    import logging
    import os
    import time
    
    logger = logging.getLogger()
    logger.setLevel(level=logging.DEBUG)
    
    time_line = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
    
    print(os.getcwd())
    log_path=os.path.join(os.getcwd(),'message')
    
    logfile = log_path + '/'+time_line + '.txt'
    
    handler = logging.FileHandler(logfile,mode='w') # 输出到log文件的handler
    # handler.setLevel(logging.INFO)
    formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
    handler.setFormatter(formatter)
    
    console = logging.StreamHandler() # 输出到console的handler
    console.setLevel(logging.WARNING)
    
    logger.addHandler(handler)
    logger.addHandler(console)
    
    logger.debug('This is a debug message.')
    logger.info('This is an info message.')
    logger.warning('This is a warning message.')
    logger.error('This is an error message.')
    logger.critical('This is a critical message.')
    
    

    DEBUG:打印全部的日志。详细的信息,通常只出现在诊断问题上。
    INFO:打印 INFO、WARNING、ERROR、CRITICAL 级别的日志。确认一切按预期运行。
    WARNING:打印 WARNING、ERROR、CRITICAL 级别的日志。表明一些问题在不久的将来,这个软件还能按预期工作。
    ERROR:打印 ERROR、CRITICAL 级别的日志。更严重的问题,软件没能执行一些功能。
    CRITICAL:打印 CRITICAL 级别。一个严重的错误,表明程序本身可能无法继续运行。

    如果不设置Level,默认输出所有信息;如果设置Level为logging.WARNING,则输出WARNING级别以下的所有信息。

    输出结果如下:
    console

    ssh://root@[my-ip-address]:22/root/miniconda3/envs/py36/bin/python -u /tmp/pycharm_project_982/pytorch-openpose-master/test.py
    /tmp/pycharm_project_982/pytorch-openpose-master
    This is a warning message.
    This is an error message.
    This is a critical message.
    
    Process finished with exit code 0
    

    202203311424.txt

    2022-03-31 14:24:54,285 - test.py[line:30] - DEBUG: This is a debug message.
    2022-03-31 14:24:54,285 - test.py[line:31] - INFO: This is an info message.
    2022-03-31 14:24:54,285 - test.py[line:32] - WARNING: This is a warning message.
    2022-03-31 14:24:54,286 - test.py[line:33] - ERROR: This is an error message.
    2022-03-31 14:24:54,286 - test.py[line:34] - CRITICAL: This is a critical message.
    
    

    更实用的方法(一次实现,全局调用)

    为了更方便的使用logging模块输出日志,可以定义一个函数或类并添加在系统变量中,这样随时可以调用该函数(或类)。

  • loggers.py(相比于上面的实现,添加了projectpath()函数用来读取工程的根目录,添加了colorlog模块用来更改不同级别日志输出的颜色和明亮程度)
  • import logging
    import os
    import time
    import colorlog
    
    # 这里是为了永远将日志文件夹放在当前工程目录下,而不至于当项目下有多个子目录时
    def projectpath():
        pwd = os.getcwd()
        while(len(pwd.split('\\'))>4):
            pwd = os.path.dirname(pwd) # 向上退一级目录
        # print(pwd)
        return pwd
    
    def __logfun(isfile=False):
        # black, red, green, yellow, blue, purple, cyan(青) and white, bold(亮白色)
        log_colors_config = {
            'DEBUG': 'bold_white',
            'INFO': 'bold',
            'WARNING': 'yellow',
            'ERROR': 'red',
            'CRITICAL': 'bold_red', # 加bold后色彩变亮
        }
        logger = logging.getLogger()
        # 输出到console
        # logger.setLevel(level=logging.DEBUG)
        logger.setLevel(level=logging.INFO) # 某些python库文件中有一些DEBUG级的输出信息,如果这里设置为DEBUG,会导致console和log文件中写入海量信息
        console_formatter = colorlog.ColoredFormatter(
            # fmt='%(log_color)s[%(asctime)s.%(msecs)03d] %(filename)s -> %(funcName)s line:%(lineno)d [%(levelname)s] : %(message)s',
            fmt='%(log_color)s %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
            # datefmt='%Y-%m-%d  %H:%M:%S',
            log_colors=log_colors_config
        )
        console = logging.StreamHandler()  # 输出到console的handler
        # console.setLevel(logging.DEBUG)
        console.setFormatter(console_formatter)
        logger.addHandler(console)
        # 输出到文件
        if isfile:
            # 设置文件名
            time_line = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
            log_path=os.path.join(projectpath(),'log')
            if not os.path.exists(log_path):
                os.mkdir(log_path)
            logfile = log_path + '/'+time_line + '.txt'
            # 设置文件日志格式
            filer = logging.FileHandler(logfile,mode='w') # 输出到log文件的handler
            # filer.setLevel(level=logging.DEBUG)
            file_formatter = logging.Formatter(
                fmt='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                datefmt='%Y-%m-%d  %H:%M:%S'
            )
            # formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
            filer.setFormatter(file_formatter)
            logger.addHandler(filer)
    
        return logger
    log=__logfun()
    if __name__=='__main__':
        log.debug('This is a debug message.')
        log.info('This is an info message.')
        log.warning('This is a warning message.')
        log.error('This is an error message.')
        log.critical('This is a critical message.')
    
  • 注意projectpath()函数是为了永远将日志文件夹放在当前工程目录下,而不至于当项目下有多个子目录时,每个子目录下都分别有各自的日志(会很混乱)。

  • 因为我所有的python项目都是在E:\project_file\PyCharm文件夹下放着的,例如E:\project_file\PyCharm\pytorch,所以在函数中我定义循环继续的条件为while(len(pwd.split('\\'))>4)(你需要根据你自己的项目文件夹更改一个数字),这样log文件的绝对路径就会永远是E:\project_file\PyCharm\pytorch\log。如果我换了一个项目project1,那log文件的绝对路径就会变为E:\project_file\PyCharm\project1\log

  • loggers.py放在D:\pythonpath(可自行更改)目录下,然后在系统环境变量PYTHONPATH中添加D:\pythonpath(如果没有这个环境变量,新建一个即可)

  • 此时就可以在任意python项目的python文件下from loggers import log了,由于os.getcwd()返回的是调用loggers的文件路径,所以不用担心日志会保存到loggers.py本身所在路径。

  • 开始搭建项目时,可能只是想在控制台看一下输出信息,直接在原始文件中定义isfile=False即可。当整个项目很大时,可能需要保存历史日志,此时在原始文件中定义isfile=True即可,非常方便。

  • 注意,以后任何用到print的地方,用log.info替换即可,(warning、error、critical根据情况选择使用即可)。log.debug不建议使用(注释中有说明)。

  • 来源:ki_rui

    物联沃分享整理
    物联沃-IOTWORD物联网 » python保存日志文件

    发表评论