7-06 271 views
一、日志输出合理的必要性
日式收集使我们日常工作中都会遇到的问题,而一个好的日志输出,则会给收集工作带来大大的效率提升。同时能够给程序员自己排错带来方便。那么python程序中logging模块是如何实现规范的日志信息输出呢,让我们深入了解下。
二、简单的将日志输出到控制台,了解日志的相关级别设定
DEBUG:程序调试bug时使用
INFO:程序正常运行时使用
WARNING:程序未按预期运行时使用,但并不是错误,如:用户登录密码错误
ERROR:程序出错误时使用,如:IO操作失败
CRITICAL:特别严重的问题,导致程序不能再继续运行时使用,如:磁盘空间为空,一般很少使用
默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。
日志等级从低到高的顺序是: DEBUG < INFO < WARNING < ERROR < CRITICAL
下面我这有个小例子。
import logging
#日志时间,主题,级别及日志信息(设置level=logging.INFO 则大于等于info级别的信息才输出)
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
#日志主题定义
logger= logging.getLogger("日志主题")
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")
logger.error("oh this is fuck error")
可以看出只有debug信息没输出来。
logging.basicConfig函数各参数:
filename:指定日志文件名;
filemode:和file函数意义相同,指定日志文件的打开模式,’w’或者’a’; 写入和追加写入
format:指定输出的格式和内容,format可以输出很多有用的信息,
format参数:作用
%(levelno)s:打印日志级别的数值
%(levelname)s:打印日志级别的名称
%(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s:打印当前执行程序名
%(funcName)s:打印日志的当前函数
%(lineno)d:打印日志的当前行号
%(asctime)s:打印日志的时间
%(thread)d:打印线程ID
%(threadName)s:打印线程名称
%(process)d:打印进程ID
%(message)s:打印日志信息
datefmt:指定时间格式,同time.strftime();
level:设置日志级别,默认为logging.WARNNING;
stream:指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略;
三、将日志写入到文件里面,产生模拟的日志文件
创建一个FileHandler,并对输出消息的格式进行设置,将其添加到logger,然后将日志写入到指定的文件中
#!/usr/bin/env python3 # encoding: utf-8 """ @version: Python 3.8.2 @author: ycy @file: log-test.py @time: 2020/6/20 16:16 """ import logging #日志主题定义 logger= logging.getLogger("__name__") #设置日志级别 logger.setLevel(level = logging.DEBUG) #日志写入路径 handler = logging.FileHandler("log.txt",'a',encoding='utf-8') #设置日志级别 handler.setLevel(level=logging.DEBUG) #日志时间,主题,级别及日志信息 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) logger.info("Start print log") logger.debug("Do something") logger.warning("Something maybe fail.") logger.error("Something maybe Error") logger.info("Finish")
可以看到文件里debug–>error的信息都有写入
四、在介绍下logging的鸡肋功能RotatingFileHandler日志回滚
为啥说它是鸡肋功能呢?它通过backupCount设定值备份日志的值,这种会丢失(N+1)个日志文件大小之前的日志信息。
比如backupCount设置为3,那它只会保留4份日志信息。超过你设定的日志大小阈值时,会清理掉最后一个日志文件
#!/usr/bin/env python3 # encoding: utf-8 """ @version: Python 3.8.2 @author: ycy @file: log-test.py @time: 2020/6/20 16:16 """ import logging from logging.handlers import RotatingFileHandler #日志主题定义 logger= logging.getLogger("__name__") #设置日志级别 logger.setLevel(level = logging.DEBUG) #定义一个RotatingFileHandler,最多备份3个日志文件,每个日志文件最大1k(方便测试将大小调整的比较小) rHandler = RotatingFileHandler("log.txt",maxBytes = 1*1024,backupCount = 3) rHandler.setLevel(logging.DEBUG) #日志时间,主题,级别及日志信息 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') rHandler.setFormatter(formatter) logger.addHandler(rHandler) logger.info("Start print log") logger.debug("Do something") logger.warning("Something maybe fail.") logger.error("Something maybe Error") logger.info("Finish")
可以看到只保留了3个日志文件备份。之前的日志会被清理。若你要无限保留,可以将backupCount及maxBytes值调整的很大,但是意义不大,我的建议是不使用这种日志回滚方式,因为存在日志丢失的情况。建议将日志输出文件已日期时间变量命名,这种既能保留全部日志,又能做为日志的切割使用。若不需要旧的日志还可以写个脚本,批量删除掉几个月前的日志
五、将logging写成一个类文件,方便其他程序调用它输出日志。
这个类最好有写入,也有控制台输出,这样可以方便你后期调试程序代码。不然你看报错还得去日志文件里看,但是后期调试完毕,需要记得在程序文件将终端输出的命令注释掉。
import logging class Loggerfile(object): def __init__(self,file_path,level,topic=None): file_handler = logging.FileHandler(file_path,'a',encoding='utf-8') fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s") file_handler.setFormatter(fmt) self.logger = logging.Logger(topic,level=level) self.logger.addHandler(file_handler) def debug(self,msg): self.logger.debug(msg) def info(self,msg): self.logger.info(msg) def warning(self,msg): self.logger.warning(msg) def error(self,msg): self.logger.error(msg) class Loggerconsole(object): def __init__(self,level,topic=None): console_handler = logging.StreamHandler() fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s") console_handler.setFormatter(fmt) self.logger = logging.Logger(topic,level=level) self.logger.addHandler(console_handler) def debug(self,msg): self.logger.debug(msg) def info(self,msg): self.logger.info(msg) def warning(self,msg): self.logger.warning(msg) def error(self,msg): self.logger.error(msg) if __name__ == '__main__': logger = Loggerfile('test.log',logging.DEBUG,'日志主题') logger.info('我是日志信息info') # logger.error('我是日志信息Error') # logger.debug('我是日志信息Debug') loggerconsole = Loggerconsole(logging.INFO,'终端日志输出主题') loggerconsole.info('我是终端输出日志info') 可以看到我定义了2个类,一个日志写入类,一个终端输出类。 下面我使用其他脚本,调用这个类文件 #!/usr/bin/env python3 # encoding: utf-8 """ @version: Python 3.8.2 @author: ycy @file: logimport.py.py @time: 2020/7/2 11:02 """ import logging import time #导入类文件(就是上面的py文件名称,在同一级目录下,不在同一级目录下需要使用from XXX目录名 import py脚本名) import logtest now_time = time.strftime("%Y%m%d", time.localtime(time.time())) def running(): #调用写入类 logger=logtest.Loggerfile('{}-test.log'.format(now_time),logging.DEBUG,'我是调用的日志主题') logger.debug("我是调用的一个debug信息") logger.info("我是调用的一个Info信息") #调用终端输出类 loggerconsole=logtest.Loggerconsole(logging.INFO,'我是调用的日志主题console终端输出') loggerconsole.info('我是调用的日志console终端info信息') if __name__ == '__main__': running()
我这里通过的是now_time这个变量写入日志文件,保证日志文件完整,有能按日切割。
好的,logging模块的简单使用就到这里了,感谢大家阅读。
版权属于: 抓不住的疯
原文地址: https://www.ycy114.com/index.php/2020/07/06/logging%e6%97%a5%e5%bf%97%e6%a8%a1%e5%9d%97/
转载时必须以链接形式注明原始出处及本声明。