1 引言

最近在开发一个应用软件,为方便调试和后期维护,在代码中添加了日志,用的是Python内置的logging模块,看了许多博主的博文,颇有所得。不得不说,有许多博主大牛总结得确实很好。似乎我再写关于logging的博文有些多余,但不写总结又总觉得没掌握。那就写写吧,也方便日后回顾。
开始总结之前,先感谢几位博主,他们的博客写得很是详尽:
说说为什么需要添加日志?
就像上面说的,为了调试和后期维护方便。也许在开发中没有太大体会,但是如果将软件部署到了生产环境中,一旦出现bug,没有日志,就很难对当时的情况进行追踪,有了日志,就可以根据日志尽可能的对当时的数据环境进行还原,方便debug。甚至,只要日志设计得足够合理,还可以用于后续业务数据分析等。

2 日志等级

为什么需要对日志进行划分等级呢?
当我们出于开发时debug的目的使用日志时,我们自然是想尽可能详尽得记录日志,但是如果部署到生产环境中,这样做就可能因为大量的IO占用服务器资源,所以在生产环境中就只需要记录异常信息、错误情况等就好了。
所以,给日志设置等级,可以方便得、因地制宜控制日志输出。
这里只介绍Python的logging模块的日志等级(当然,其他日志系统的日志等级划分事实上也基本相同)。logging的日志等级包括5个:
日志等级(level)
描述
DEBUG
最详细的日志信息,典型应用场景是 问题诊断
INFO
信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
WARNING
当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
ERROR
由于一个更严重的问题导致某些功能不能正常运行时记录的信息
CRITICAL
当发生严重错误,导致应用程序不能继续运行时记录的信息
日志等级从上到下依次提高,当在程序中设定某个日志等级之后,比设定的日志等级低的日志记录将会被忽略,即logging就只会输出大于和等于设定的等级的日志。我们将在下文中通过代码示例证明这一点。

3 记录日志

logging模块提供两种方法记录日志:
(1)通过logging模块提供的模块级函数记录日志;
(2)通过logging模块提供的4大组件记录日志。

3.1 记录日志之logging模块级函数

在logging模块中,分别给出一个模块级别函数与上面说到的日志级别相对应,用于输出对应级别日志记录:
函数
说明
logging.debug(msg, *args, **kwargs)
创建一条严重级别为DEBUG的日志记录
logging.info(msg, *args, **kwargs)
创建一条严重级别为INFO的日志记录
logging.warning(msg, *args, **kwargs)
创建一条严重级别为WARNING的日志记录
logging.error(msg, *args, **kwargs)
创建一条严重级别为ERROR的日志记录
logging.critical(msg, *args, **kwargs)
创建一条严重级别为CRITICAL的日志记录
也有一个函数汇总了上面5个函数的功能:
函数
说明
logging.log(level, *args, **kwargs)
创建一条严重级别为level的日志记录
现在可以来尝试使用一下上面的函数了:
  1. import logging
  2.  
  3. logging.debug('debug')
  4. logging.info('info')
  5. logging.warn('warn')
  6. logging.error('error')
  7. logging.critical('critical')
  8. logging.warn('Today is %s',datetime.date.today())
运行结果如下:
WARNING:root:warn
ERROR:root:error
CRITICAL:root:critical
WARNING:root:Today is 2019-03-28
 上面的函数都有*args, **kwargs这两个参数,所以这些函数可以接受任意位置参数和关键字参数,这些参数填充到第一个参数msg,最后一条日志输出中添加了当前日期就是利用了这个功能。
那为什么会只输出后面3条日志记录呢?上面说到过,logging就只会输出大于和等于设定的等级的日志记录,而logging的默认日志等级是WARNING,所以日志等级为DEBUG和INFO的两条记录都没有被输出。
如果想要输入日志等级为DEBUG和INFO的日志记录,就要对logging进行配置。logging也提供了一个模块级别的专用于配置logging的函数:
函数
说明
logging.basicConfig(**kwargs)
对root logger进行一次性配置
尝试使用一下这个配置函数:
  1. import logging
  2. logging.basicConfig(level=logging.DEBUG) # 设置日志等级
  3. logging.debug('debug')
  4. logging.info('info')
  5. logging.warn('warn')
  6. logging.error('error')
  7. logging.critical('critical')
运行结果如下:
DEBUG:root:debug
INFO:root:info
WARNING:root:warn
ERROR:root:error
CRITICAL:root:critical
看,日志等级为DEBUG和INFO的两条记录也都得到了输出。
上面表格对logging.basicConfig函数的说明中指出,logging.basicConfig函数时一次性配置,什么意思呢?意思就是说,logging.basicConfig函数只在第一次运行(第一次对logging进行配置)时起作用,后面在此设置其他参数是不会生效的。通过代码证明一下:
  1. import logging
  2. logging.basicConfig(level=logging.DEBUG) # 设置日志等级
  3. logging.basicConfig(level=logging.INFO) # 重新设置日志等级
  4. logging.debug('debug')
  5. logging.info('info')
  6. logging.warn('warn')
  7. logging.error('error')
  8. logging.critical('critical')
运行结果:
DEBUG:root:debug
INFO:root:info
WARNING:root:warn
ERROR:root:error
CRITICAL:root:critical
看到没,DEBUG级别日志记录还是输出了,证明重新运行logging.basicConfig函数设置日志级别没有生效。
另外需要注意的是,一定要在使用logging记录日志之前使用logging.basicConfig进行配置,否则,不会有任何输出。
我们再观察一下上面的程序输出,可以发现,每一条输出的结果里,不仅仅只有我们输出的字符串参数,还有其它的一些信息,例如日志等级,日志器名称(默认是root),分隔符(这里是冒号)等,这些都是logging默认给我配置好的,当然,我们也可以通过logging.basicConfig函数的各参数自定义logging的输出。
参数名称
描述
filename
指定日志输出目标文件的文件名,指定该设置项后日志信心就不会被输出到控制台了
filemode
指定日志文件的打开模式,默认为'a'。需要注意的是,该选项要在filename指定时才有效
format
指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出。
datefmt
指定日期/时间格式。需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效
level
指定日志器的日志级别
stream
指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常
style
Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为'%'、'{'和'$',默认为'%'
handlers
Python 3.3中新添加的配置项。该选项如果被指定,它应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常。
上表中的参数format可以通过logging模块中定义好模式来设定值:
字段/属性名称
使用格式
描述
asctime
%(asctime)s
将日志的时间构造成可读的形式,默认情况下是‘2019-03-28 00:00:00,000’的形式,精确到毫秒
name
%(name)s
所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger
filename
%(filename)s
调用日志输出函数的模块的文件名; pathname的文件名部分,包含文件后缀
funcName
%(funcName)s
由哪个function发出的log, 调用日志输出函数的函数名
levelname
%(levelname)s
日志的最终等级(被filter修改后的)
message
%(message)s
日志信息, 日志记录的文本内容
lineno
%(lineno)d
当前日志的行号, 调用日志输出函数的语句所在的代码行
levelno
%(levelno)s
该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
pathname
%(pathname)s
完整路径 ,调用日志输出函数的模块的完整路径名,可能没有
process
%(process)s
当前进程, 进程ID。可能没有
processName
%(processName)s
进程名称,Python 3.1新增
thread
%(thread)s
当前线程, 线程ID。可能没有
threadName
%(thread)s
线程名称
module
%(module)s
调用日志输出函数的模块名, filename的名称部分,不包含后缀即不包含文件后缀的文件名
created
%(created)f
当前时间,用UNIX标准的表示时间的浮点数表示; 日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值
relativeCreated
%(relativeCreated)d
输出日志信息时的,自Logger创建以 来的毫秒数; 日志事件发生的时间相对于logging模块加载时间的相对毫秒数
msecs
%(msecs)d
日志事件发生事件的毫秒部分。logging.basicConfig()中用了参数datefmt,将会去掉asctime中产生的毫秒部分,可以用这个加上
所以,结合上表中的内容,我们可以实现让每一条日志记录输出事件发生时间、事件发生位置、日志级别、事件内容等信息。
现在,我们来给刚才的日志添加一些输出,例如每条日志输出日志时间、日志级别、所在模块名、函数名、行号等信息,并指定时间输出格式,最后把日志输出到当前目录下的.log文件中。代码如下:
  1. import logging
  2. fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
  3. datefmt = '%Y-%m-%d %H:%M:%S %a'
  4. logging.basicConfig(level=logging.DEBUG,
  5. format=fmt,
  6. datefmt=datefmt,
  7. filename=".log")
  8. logging.debug('debug')
  9. logging.info('info')
  10. logging.warn('warn')
  11. logging.error('error')
  12. logging.critical('critical')
运行上述代码后,控制台不会再有输出了,但当前目录下的.log文件会写入一下内容:
2019-03-28 16:34:08 Thu , DEBUG , log_test.py <module> line 8 , debug
2019-03-28 16:34:08 Thu , INFO , log_test.py <module> line 9 , info
2019-03-28 16:34:08 Thu , WARNING , log_test.py <module> line 10 , warn
2019-03-28 16:34:08 Thu , ERROR , log_test.py <module> line 11 , error
2019-03-28 16:34:08 Thu , CRITICAL , log_test.py <module> line 12 , critical

3.2 记录日志之logging四大组件

logging四大组件是logging日志记录的高级用法。四大组件包括Logger、Handelr、Filter、Formater,且都是以类的形式来使用。logging四大组件协同工作流如下如所示:
各组件功能如下:
组件名称
对应类名
功能描述
日志器
Logger
用于提供日志接口,常用于配置和发送日志消息
处理器
Handler
用于写入日志并输出到指定位置,例如控制台、文件或网络位置等
过滤器
Filter
对日志记录进行进一步过滤,输出符合条件的日志记录
格式器
Formatter
配置日志记录的最终输出格式
(1)日志器:Logger
日志器Logger以工厂化的形式返回一个Logger类实例。一般而言,大多使用下面的方法获得Logger类实例:
logging.getLogger(name)
属性name是为Logger实例指定的名称,如果使用同一个名称进行实例化,则实际上只是将后面实例对象名指向前面的同名Logger实例。在使用logging模块时,系统会自动实例化一个名为root的日志器(根日志器),当未指定name属性时,事实上就是将变量名指向跟日志器
另外,Logger实例具有层级继承的特点,层级之间已“.”连接,例如:“a.b”,“a.b.c”,a是父日志器,b是子日志器,在未对子日志器进行配置情况下,子日志器默认继承父日志器的配置,对子日志器重新配置不会影响父日志器。这一点很重要,在多模块中记录日子是可以使用这一特性,我们在下文代码中实践这一特性。根日志器是所有日志器的默认父日志器。
Logger类还有以下的常用方法:
  • logger.setLevel() :设置日志器处理日志信息的最低级别
  • logger.addHandler():为该logger对象添加一个handler对象
  • logger.removeHandler():为该logger对象添加移除一个handler对象
  • logger.addFilter():为该logger对象添加一个filter对象
  • logger.removeFilter():为该logger对象移除一个filter对象
  • logger.debug(),logger.info(),logger.warning(),logger.error(),logger.critical():创建一个对应等级的日志记录
(2)处理器:Handler
Handler实例用于将日志记录发送到指定的位置进行输出。一个logger对象可以添加多个handler(例如既要在控制台输出日志,又要将日志写入到文件A,还要讲日志写入文件B,这就可以配置3个handler),每个handler又可以定义不同日志级别,以实现日志分级过滤显示。常用的方法包括:
  • handler.setLevel():设置handler处理的日志信息最低级别
  • handler.setFormatter():为handler设置一个格式器对象
  • handler.addFilter():为handler添加一个过滤器对象
  • handler.removeFilter():为handler删除一个过滤器对象
要注意的是,在实际开发中,最好不要直接使用Handler类,应根据实际的功能需要,实例化Handler类的子类。Handler类的之类包括:
Handler
描述
logging.StreamHandler
将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。
logging.FileHandler
将日志消息发送到磁盘文件,默认情况下文件大小会无限增长
logging.handlers.RotatingFileHandler
将日志消息发送到磁盘文件,并支持日志文件按大小切割
logging.hanlders.TimedRotatingFileHandler
将日志消息发送到磁盘文件,并支持日志文件按时间切割
logging.handlers.HTTPHandler
将日志消息以GET或POST的方式发送给一个HTTP服务器
logging.handlers.SMTPHandler
将日志消息发送给一个指定的email地址
logging.NullHandler
该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免'No handlers could be found for logger XXX'信息的出现。
通过代码来演示一下,功能如下:在控制台输出日志(日志级别为debug),同时将日志写入到文件a.log文件(日志级别为debug),还要讲日志写入文件b.log文件(日志级别为warn):
  1. import logging
  2.  
  3. logger = logging.getLogger()
  4. logger.setLevel(logging.DEBUG)
  5.  
  6. # 控制台输出
  7. con_handler = logging.StreamHandler()
  8. con_handler.setLevel(logging.INFO)
  9. logger.addHandler(con_handler)
  10.  
  11. # 输出到文件a.log
  12. file_a_handler = logging.FileHandler('./a.log', encoding='UTF-8')
  13. file_a_handler.setLevel(logging.DEBUG)
  14. logger.addHandler(file_a_handler)
  15.  
  16. # 输出到文件b.log
  17. file_b_handler = logging.FileHandler('./b.log', encoding='UTF-8')
  18. file_b_handler.setLevel(logging.WARNING)
  19. logger.addHandler(file_b_handler)
  20.  
  21. if __name__=='__main__':
  22. logger.debug('debug msg')
  23. logger.info('info msg')
  24. logger.warning('warn msg')
运行上面代码后,控制台输出如下:
info msg
warn msg
文件a.log会写入一下内容:
debug msg
info msg
warn msg
文件b.log会写入以下内容:
warn msg
注意:在一个日志器中添加多个handler时要注意,最好通过logger.setLevel(logging.DEBUG)先设置一下logger本身的日志级别,如果某个handler的级别比logger的日志级别低,那么该handler的日志级别无效,handler会以logger的级别来处理。
(3)格式器:Formatter
Formatter类实例用于配置日志记录的内容、结构等信息。可以通过以下三个参数进行配置:
  • fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值
  • datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"
  • style:指定格式化占位符,可取值为 '%', '{'和 '$',如果不指定该参数则默认使用'%'
fmt的使用方法可以参照上文中介绍过的logging.basicConfig函数format参数的配制方法。
例:每条日志输出日志时间、日制定及、所在模块名、函数名、行号等信息,并指定时间输出格式,最后把日志输出到控制台。代码如下:
  1. import logging
  2.  
  3. logger = logging.getLogger(__name__)
  4. handler = logging.StreamHandler()
  5. logger.setLevel(logging.DEBUG)
  6. # 定义格式器,添加到处理器中
  7. fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
  8. datefmt = '%Y-%m-%d %H:%M:%S %a'
  9. log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
  10. handler.setFormatter(log_fmt)
  11.  
  12. logger.addHandler(handler)
  13.  
  14. logger.debug('debug msg')
  15. logger.info('info msg')
控制台输出如下:
2019-03-29 19:36:03 Fri , DEBUG , log_test2.py <module> line 14 , debug msg
2019-03-29 19:36:03 Fri , INFO , log_test2.py <module> line 15 , info msg
 
(4)过滤器:Filter
在我们已经知道的logging使用方法中,都是通过日志级别来控制日志是否输出,Filter能够实现更加强大的过滤功能,控制日志输出。自定义的过滤器中必须覆写filter方法,当filter的返回值判断为True则允许输出,反之不允许输出。例如过滤包含敏感信息的日志,过滤器定义如下:
  1. import logging
  2.  
  3. class CountryFilter(logging.Filter):
  4. def filter(self,record):
  5. return "America" not in record.getMessage()
  6.  
  7. logger = logging.getLogger()
  8. handler = logging.StreamHandler()
  9. logger.addHandler(handler)
  10. logger.setLevel(logging.DEBUG)
  11. logger.addFilter(CountryFilter())
  12.  
  13. logger.critical('I love America')
  14. logger.debug('I love China')
输出结果:
I love China
可以看到,虽然第一条日志记录的日志等级更高,但是因为包含了过滤器中包含的敏感信息,所以不被允许输出。

4 logging奇淫巧技

4.1 记录异常信息:捕获traceback

如果在日志中,只是记录发生了异常,那其实作用不大,如果traceback也记录到日志中,那就完美了。强大的logging确实也提供了这一功能,而且使用也很简单:
  1. import logging
  2.  
  3. logger = logging.getLogger(__name__)
  4. handler = logging.FileHandler('./.log',encoding='utf-8')
  5. logger.setLevel(logging.DEBUG)
  6. # 定义格式器,添加到处理器中
  7. fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
  8. datefmt = '%Y-%m-%d %H:%M:%S %a'
  9. log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
  10. handler.setFormatter(log_fmt)
  11.  
  12. logger.addHandler(handler)
  13.  
  14. try:
  15. logger.info('Running …')
  16. 1/0
  17. except Exception as e:
  18. logger.error('Exception occurs!',exc_info = True)
  19. # logger.exception(e) # 与上面这行效果一样
运行后,文件.log会被写入以下内容:
2019-03-29 19:53:14 Fri , INFO , log_test2.py <module> line 15 , Running …
2019-03-29 19:53:14 Fri , ERROR , log_test2.py <module> line 18 , Exception occurs!
Traceback (most recent call last):
File "E:/myCode/test1/log_test2.py", line 16, in <module>
1/0
ZeroDivisionError: division by zero

4.2 多模块共享日志

在开发过程中,经常出现多个模块都需要记录日志的情况,也许你想到的做法是在一个模块中配置好一个logger并实例化,在需要用到的模块中进行导入,但如果不同模块的日志器配置有区别时,这种方法就不适用了,若是为每个模块都定义一个logger,所有配置都需要重新写入,有些繁琐。还记得上文中提到logging的日志器可以通过name属性进行分层吗?子日志器可以继承父日志器的配置,也可以重新配置,这就是logging给我们提供的多模块共享日志的解决方案。看代码:
模块main.py中的代码:
  1. import logging
  2. import log_child
  3. logger = logging.getLogger('main')
  4. logger.setLevel(logging.DEBUG)
  5.  
  6. fmt = '%(name)s , %(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
  7. datefmt = '%Y-%m-%d %H:%M:%S %a'
  8. log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
  9.  
  10. handler = logging.FileHandler('./.log',encoding='utf-8')
  11. handler.setFormatter(log_fmt)
  12.  
  13. logger.addHandler(handler)
  14.  
  15. if __name__=='__main__':
  16. logger.debug('Running …')
  17. log_child.fun_child()
模块child_log.py中的代码:

  1. import logging
  2.  
  3. logger = logging.getLogger('main.child')
  4. logger.setLevel(logging.DEBUG)
  5. def fun_child():
  6. try:
  7. logger.info('Running …')
  8. 1 / 0
  9. except Exception as e:
  10. logger.exception(e)
运行main.py后,.log文件会被写入一下内容:
main , 2019-03-29 20:23:32 Fri , DEBUG , main.py <module> line 16 , Running …
main.child , 2019-03-29 20:23:32 Fri , INFO , log_child.py fun_child line 7 , Running …
main.child , 2019-03-29 20:23:32 Fri , ERROR , log_child.py fun_child line 10 , division by zero
Traceback (most recent call last):
File "E:\myCode\test1\log_child.py", line 8, in fun_child
1 / 0
ZeroDivisionError: division by zero

4.3 使用配置文件配置logger

我们之前的程序中都是将对logger的配置一并写在程序中,但事实上,采用配置化编程的方式,将对logger的配置写在专门的配置文件中,例如写入json文件、conf文件、yaml文件等文件中,当需要实例化logger时,读取即可。下面以conf文件为例,通过代码注释的方式,介绍logging配置文件的书写方式。配置文件log.conf如下:

  1. [loggers] #固定写法,定义logger的模块
  2. keys=root,log_1,log_2 #创建三个logger,root是父类,必需存在的,其他两个logger的name分别为
  3.  
  4. [logger_root] # 定制上面的logger,严格要求格式为"logger_loggername",必须通过loggername与上面的logger一一对应
  5. level=DEBUG # 设置日志级别
  6. qualname=root # 对于root,其实这里可以不填,默认就是root
  7. handlers=debugs #设置指定处理器,如果有多个处理器,中间以逗号分隔,这个名字待会儿 我们会以固定格式"handler_(value)"创建
  8.  
  9. [logger_log_1]
  10. level=INFO
  11. qualname=log_1 #除了root以外,必须要设置这个属性,用于定义打印输出时候的logger名
  12. handlers=infos
  13. propagate=0 # 是否将消息想父日志传递,0表示不传递,1表示传递。如果向上传递,父日志器接收到消息后会以父日志器的配置再次处理该消息,所以可能所有多次输出
  14.  
  15. [logger_log_2]
  16. level=WARNING
  17. qualname=log_2
  18. handlers=warns
  19.  
  20. [handlers] #固定格式, 开始定义处理器
  21. keys=debugs,infos,warns#定义过滤器名称,与上面出现的handlers的值一一对应,下面定义以handler_handlername格式定义
  22.  
  23. [handler_debugs]
  24. class=StreamHandler # 指定处理器的类名
  25. level=DEBUG # 设置级别
  26. formatter=form01 #定义格式器,名称为form01,下面会创建formatters,格式也是严格要求为formatter_formattername
  27. args=(sys.stdout,) # 控制台输出
  28.  
  29. [handler_infos]
  30. class=FileHandler
  31. level=INFO
  32. formatter=form02
  33. args=('b.log','a')
  34.  
  35. [handler_warns]
  36. class=FileHandler
  37. level=WARNING
  38. formatter=form02
  39. args=('a.log','a')# 写入到文件,写入方式
  40.  
  41. [formatters] #固定格式
  42. keys=form01,form02 #定义名称,下面会引用格式,与上面出现的formatter的值对应
  43.  
  44. [formatter_form01]
  45. format=%(asctime)s %(message)s # 定义消息输出格式,内容
  46. datefmt=%Y-%m-%d %H:%M:%S #日期输出格式
  47.  
  48. [formatter_form02]
  49. format=%(asctime)s %(filename)s %(levelname)s %(message)s
  50. datefmt=%Y-%m-%d %H:%M:%S
实例化logger:
  1. # _*_coding:utf-8_*_
  2. import logging
  3. from logging.config import fileConfig
  4.  
  5. fileConfig('log.conf')
  6. root= logging.getLogger(name="root")
  7. log_1= logging.getLogger(name="log_1")
  8. log_2= logging.getLogger(name="log_2")
  9.  
  10. root.debug('root_debug')
  11. root.info('root_info')
  12. root.warning('root_warning')
  13. log_1.debug('log_1_debug')
  14. log_1.info('log_1_info')
  15. log_1.warning('log_1_warning')
  16. log_2.debug('log_2_debug')
  17. log_2.info('log_2_info')
  18. log_2.warning('log_2_warning')
程序运行后,控制台输出如下:
2019-03-29 21:43:24 root_debug
2019-03-29 21:43:24 root_info
2019-03-29 21:43:24 root_warning
a.log文件将被写入以下内容:
2019-03-29 21:43:24 main.py INFO log_1_info
2019-03-29 21:43:24 main.py WARNING log_1_warning
b.log文件将被写入以下内容:
2019-03-29 21:43:24 main.py WARNING log_2_warning

4.3 日志回滚

什么是日志回滚呢?咋一听,好像不知道是什么东西。日志回滚就是按照日期或者时间(有时候甚至是日志和时间综合作用),对日志进行分割或者删除。实际开发中经常需要用到,因为随着应用的持续运行,日志文件会越来越庞大,对系统的性能产生影响,所以有必要删除早起的日志。
logging中提供了两个处理器用于日志回滚,一个是RotatingFileHandler,它主要是根据日志文件的大小进行滚动,另一个是TimeRotatingFileHandler,它主要是根据时间进行滚动。
(1)根据文件大小进行回滚
按文件大小回滚的类是RotatingFileHandler:

  1. # -*- coding:utf-8 -*-
  2. import logging
  3. from logging.handlers import RotatingFileHandler
  4.  
  5. logger = logging.getLogger('main')
  6. logger.setLevel(level = logging.INFO)
  7. # 定义一个RotatingFileHandler,最多备份三个日志文件, 每个日志文件最大1k
  8. file_handler = RotatingFileHandler(".log",maxBytes = 1*1024,backupCount = 3)
  9.  
  10. file_handler.setLevel(logging.INFO)
  11. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  12. file_handler.setFormatter(formatter)
  13.  
  14. cons_handler = logging.StreamHandler()
  15. cons_handler.setLevel(logging.DEBUG)
  16. cons_handler.setFormatter(formatter)
  17.  
  18. logger.addHandler(file_handler)
  19. logger.addHandler(cons_handler)
  20.  
  21. if __name__=='__main__':
  22. while True:
  23. logger.debug("debug")
  24. logger.info("info")
  25. logger.warning("warning")
  26. logger.critical("critical")
上述程序执行后,将持续在控制台输出所有的日志记录,日志记录文件有三个,循环向日志文件中写入日志,当文件大小达到1kb时,开始在另一个文件删除日志记录,并写入新的日志记录。
(2)根据时间进行回滚。
按文件时间回滚的类时TimeRotatingFileHandler,这一个类包含以下参数:
filename :输出日志文件名的前缀,比如main.log
when 是一个字符串的定义如下:
“S”: Seconds
“M”: Minutes
“H”: Hours
“D”: Days
“W”: Week day (0=Monday)
“midnight”: Roll over at midnight
interval 是指等待多少个单位when的时间后

  1. import time
  2. import logging
  3. import logging.handlers
  4.  
  5. # logging初始化工作
  6. logging.basicConfig()
  7.  
  8. # logger的初始化工作
  9. logger = logging.getLogger('main')
  10. logger.setLevel(logging.INFO)
  11.  
  12. # 添加TimedRotatingFileHandler
  13. # 定义一个1秒换一次log文件的handler
  14. # 保留3个旧log文件
  15. timefilehandler = logging.handlers.TimedRotatingFileHandler(".log", when='S', interval=1, backupCount=3)
  16. # 设置后缀名称,跟strftime的格式一样
  17. timefilehandler.suffix = "%Y-%m-%d_%H-%M-%S.log"
  18.  
  19. formatter = logging.Formatter('%(asctime)s|%(name)-12s: %(levelname)-8s %(message)s')
  20. timefilehandler.setFormatter(formatter)
  21. logger.addHandler(timefilehandler)
  22.  
  23. while True:
  24. time.sleep(0.1)
  25. logger.debug("debug")
  26. logger.info("info")
  27. logger.warning("warning")
  28. logger.critical("critical")

5 总结

本篇系统得总结了Python内容的日志记录模块logging的用法,囊括了logging的大部分内容。掌握本篇内容,感觉在开发中基本没有问题。
参考:
 

Python开发之日志记录模块:logging的更多相关文章

  1. [ Python入门教程 ] Python中日志记录模块logging使用实例

    python中的logging模块用于记录日志.用户可以根据程序实现需要自定义日志输出位置.日志级别以及日志格式. 将日志内容输出到屏幕 一个最简单的logging模块使用样例,直接打印显示日志内容到 ...

  2. 日志记录模块logging

    在python中,日志记录显示有两种方式,一种是保存在文件和打印屏幕上,一种保存在文件中. 第一种,直接保存在文件中. import logging #日志模块,方便记录日志 # 下面是配置日志记录格 ...

  3. Python中的日志记录方案-logging模块&loguru模块

    原文链接 原创: 崔庆才 在 Python 中,一般情况下我们可能直接用自带的 logging 模块来记录日志,包括我之前的时候也是一样.在使用时我们需要配置一些 Handler.Formatter ...

  4. python爬虫学习之日志记录模块

    这次的代码就是一个日志记录模块,代码很容易懂,注释很详细,也不需要安装什么库.提供的功能是日志可以显示在屏幕上并且保存在日志文件中.调用的方式也很简单,测试代码里面有. 源代码: #encoding= ...

  5. c++日志记录模块

    C++ 日志记录模块 该模块从实际项目中产生,通过extern声明的方式,可在代码不同模块中生成日志,日志文件名称为随机码加用户指定名称,采用随机码是为了避免日志文件可能被覆盖的问题. 愿意的话你也能 ...

  6. 基于AOP和ThreadLocal实现的一个简单Http API日志记录模块

    Log4a 基于AOP和ThreadLocal实现的一个简单Http API日志记录模块 github地址 : https://github.com/EalenXie/log4a 在API每次被请求时 ...

  7. python基础语法13 内置模块 subprocess,re模块,logging日志记录模块,防止导入模块时自动执行测试功能,包的理论

    subprocess模块: - 可以通过python代码给操作系统终端发送命令, 并且可以返回结果. sub: 子    process: 进程 import subprocess while Tru ...

  8. python3 logging 日志记录模块

    #coding:utf-8 import logginglogging.basicConfig(filename='log1.log', format='%(asctime)s -%(name)s-% ...

  9. python学习笔记9--日志模块logging

    我们在写程序的时候经常会打一些日志来帮助我们查找问题,这次学习一下logging模块,在python里面如何操作日志.介绍一下logging模块,logging模块就是python里面用来操作日志的模 ...

随机推荐

  1. 使用sqlmap中tamper脚本绕过waf

    使用sqlmap中tamper脚本绕过waf 刘海哥 · 2015/02/02 11:26 0x00 背景 sqlmap中的tamper脚本来对目标进行更高效的攻击. 由于乌云知识库少了sqlmap- ...

  2. 操作系统笔记(六)页面置换算法 FIFO法 LRU最近最久未使用法 CLOCK法 二次机会法

    前篇在此: 操作系统笔记(五) 虚拟内存,覆盖和交换技术 操作系统 笔记(三)计算机体系结构,地址空间.连续内存分配(四)非连续内存分配:分段,分页 内容不多,就不做index了. 功能:当缺页中断发 ...

  3. ubuntu 系统关键指令

    1. 查看系统版本号 cat /etc/issue uname -a cat /proc/version 2. linux 32/64 bit? getconf LONG_BIT 3. dpkg 的命 ...

  4. kafka系列二、kafka manager的安装和使用

    1. Yahoo kafka manager介绍 项目地址:https://github.com/yahoo/kafka-manager Requirements: Kafka 0.8.1.1 or ...

  5. jython获取was5.1的jvm监控参数

    perfName = AdminControl.completeObjectName ('type=Perf,process=server1,node=TSC,cell=TSC,*') perfONa ...

  6. WebSphere的jython编码的一个坑

    was5.1版本,用"name=" in line这类判断字符串包含的方式时,系统会提示报错 TypeError: string member test needs char le ...

  7. 红黑树与AVL树

    概述:本文从排序二叉树作为引子,讲解了红黑树,最后把红黑树和AVL树做了一个比较全面的对比. 1 排序二叉树 排序二叉树是一种特殊结构的二叉树,可以非常方便地对树中所有节点进行排序和检索. 排序二叉树 ...

  8. 018_nginx_proxy死循环问题

    今天线上遇到一个请求一次,触发多次的请求,而且直接把nginx机器压垮了.经排查,经过如下: 一. server{ server www.jyall.com; location /latestrele ...

  9. apache服务器的常用功能及设置

    安装httpd yum -y install  httpd     服务脚本:/etc/rc.d/init.d/httpd        脚本配置文件:/etc/sysconfig/httpd     ...

  10. 转载:分布式文件系统 - FastDFS 在 CentOS 下配置安装部署(1)

    原文:http://blog.mayongfa.cn/192.html 一.安装 libfastcommon 和 FastDFS 1.下载安装 libfastcommon ,这里是通过wget下载(我 ...