一般写大型程序的时候,程序运行时间都比较长,将一些必要的输出保存到日志文件中是很有必要的。下面提供两种将输出保存到日志文件的方法,以及一种 非常实用的全局调用方法。

切换系统输出流

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

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不建议使用(注释中有说明)。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐