logging—python日志之深入浅出

xiaoxiao2021-02-28  23

关于python的日志我采用的是python自带的logging,因为它确实好用而且支持各种场景和各种用法,所以本篇详细的介绍介绍。

虽然网上有各种博文,但是思路都不是很好,基本都是先疯狂灌输一些概念,然后介绍他们之间的关系,再然后罗列代码,讲得让人似懂非懂,所以本篇想用深入浅出的方式来让大家重新理解下logging模块。

 

我们默认大家都有一定的项目经验,无论是python的还是java的,我们首先回顾下日志最基本需要哪些参数?

日志级别:从debugcritical,决定了什么样的等级才可以被log记录;

日志格式:要设计一个方便阅读的format,否则记录一坨屎一样的文本有够受的了;

日志输出:要将日志记录到哪里去,传统方式是记录到log目录下以文件方式记录,也有微服务ELK方式通过流的方式做收集。

日志内容:记录日志是为了维护时方便解决问题,所以要记录有价值的信息,我们可以通过traceback拿到堆栈内容,这个对我们修复bug有很大的帮助。

 

可能你还能想到很多其它参数,不过目前这几个已经足够我们来开发一个小程序了。

 

代码如下:

import traceback import sys import logging logging.basicConfig( level=logging.DEBUG, filename='C:\\python\\tmp\\testlog.log', filemode='w', #每次覆盖,还可以配置成追踪 format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', ) class User(object) : def __init__(self,username): self.username = username def eatfood(self,times): self.foodbag.eatfood(times) class Foodbag(object) : def __init__(self, bagname, foodlist): self.bagname = bagname self.foodlist = foodlist def eatfood(self,times): for i in range(times): print('Eat ' + str(self.foodlist.pop())) if __name__ == '__main__' : try: logging.debug('This is my logging debug information') bag = Foodbag('foodbag', ['egg', 'water', 'bread', 'milk', 'steak']) user = User('yejingtao') user.foodbag = bag user.eatfood(2) user.eatfood(1) user.eatfood(3) except IndexError as ex: print('Sorry, you have none food to eat') ty,tv,tb = sys.exc_info() logging.error('Exception info: %s' %ex) logging.error('Error Type{0}, Error Information:{1}'.format(ty,tv)) logging.error(''.join(traceback.format_tb(tb))) sys.exit(1)

执行后会生成这个log文件“C:\\python\\tmp\\testlog.log”,内容如下:

2018-03-15 16:17:01,077 quicksort.py[line:32] DEBUG This is my logging debug information 2018-03-15 16:17:01,077 quicksort.py[line:42] ERROR object info: pop from empty list 2018-03-15 16:17:01,077 quicksort.py[line:43] ERROR Error Type<class 'IndexError'>, Error Information:pop from empty list 2018-03-15 16:17:01,078 quicksort.py[line:44] ERROR File "C:/python/work/smart/pyart/quicksort.py", line 38, in <module> user.eatfood(3) File "C:/python/work/smart/pyart/quicksort.py", line 17, in eatfood self.foodbag.eatfood(times) File "C:/python/work/smart/pyart/quicksort.py", line 26, in eatfood print('Eat ' + str(self.foodlist.pop()))

 

OK 代码不需要详解了吧,第一个案例很简单。随着我代码体量越来越大,项目越来越复杂,出线了模块拆分,不同的模块我的log要记录到不同的文件里,而且日志的样式也有了差距,案例中那种直接basicConfig的写法已经不能满足我的要求了,所以我要做模块化。

 

这里需要引入handler的感念,handler对象独立定义自己的日志级别、日志存储方式、存储地址、过滤设置、日志格式等,我们的logging中维护多个handler,需要哪个的时候用哪个,互不干扰+个性化定制。 代码改造后如下: import traceback import sys import logging #__name__代表python当前模块名,你可以根据业务和模块自己手工定义 logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) handler1 = logging.FileHandler('C:\\python\\tmp\\handle1.log') handler1.setLevel(logging.INFO) handler2 = logging.FileHandler('C:\\python\\tmp\\handle2.log') handler2.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') handler1.setFormatter(formatter) handler2.setFormatter(formatter) #加入了2个handler,是因为我想每个日志记录两份 logger.addHandler(handler1) logger.addHandler(handler2) class User(object) : def __init__(self,username): self.username = username def eatfood(self,times): self.foodbag.eatfood(times) class Foodbag(object) : def __init__(self, bagname, foodlist): self.bagname = bagname self.foodlist = foodlist def eatfood(self,times): for i in range(times): print('Eat ' + str(self.foodlist.pop())) if __name__ == '__main__' : try: logger.debug('This is my logging debug information') bag = Foodbag('foodbag', ['egg', 'water', 'bread', 'milk', 'steak']) user = User('yejingtao') user.foodbag = bag user.eatfood(2) user.eatfood(1) user.eatfood(3) except IndexError as ex: print('Sorry, you have none food to eat') ty,tv,tb = sys.exc_info() logger.error('Exception info: %s' %ex) logger.error('Error Type{0}, Error Information:{1}'.format(ty,tv)) logger.error(''.join(traceback.format_tb(tb))) sys.exit(1) 运行结束后“C:\\python\\tmp\\handle1.log”和“C:\\python\\tmp\\handle2.log”都有如下日志信息: 2018-03-15 16:38:20,685 logtest.py[line:50] ERROR Exception info: pop from empty list 2018-03-15 16:38:20,686 logtest.py[line:51] ERROR Error Type<class 'IndexError'>, Error Information:pop from empty list 2018-03-15 16:38:20,686 logtest.py[line:52] ERROR File "C:/python/work/smart/pyart/logtest.py", line 46, in <module> user.eatfood(3) File "C:/python/work/smart/pyart/logtest.py", line 25, in eatfood self.foodbag.eatfood(times) File "C:/python/work/smart/pyart/logtest.py", line 34, in eatfood print('Eat ' + str(self.foodlist.pop())) 这里有有几点需要强调下: 1 Logger的名字可以任意字符,代表的是模块的意思,用“__name__”这种写法是直接使用所在python代码的模块名。 2 Logger可以绑定任意多个handler,至少要绑定一个。

3 HandlerStreamHandlerFileHandler RotatingFileHandler TimedRotatingFileHandler这些子类,其实就是两大类,第一个是流式的,可以接任何输入;后面三种分别是普通文件、按内容大小分文件、按时间分文件,都是文件式。

OK,随着我项目越来越大,我发现这种通过代码来维护的方式也不能满足我的要求了,我想把logging的配置用一个文件专门来维护,于是就有了我们的第三个案例:logging.conf[loggers] keys=root,logtest [handlers] keys=myHandler [formatters] keys=myFormatter [formatter_myFormatter] format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s [logger_logtest] level=INFO handlers=myHandler qualname=example [logger_root] level=INFO handlers=myHandler [handler_myHandler] class=FileHandler level=INFO formatter=myFormatter args=('C:\\python\\tmp\\configlog.log','a')代码只需要这两行就可以获取到loggerlogging.config.fileConfig('C:\\python\\tmp\\logging.conf') #参数要对应着配置文件里的loggers里的key logger = logging.getLogger('logtest')配置文件中同样可以配置多个logger和多个handler

转载请注明原文地址: https://www.6miu.com/read-2630077.html

最新回复(0)