前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python开发之日志记录模块:logg

Python开发之日志记录模块:logg

作者头像
py3study
发布2020-01-16 11:12:58
1.2K0
发布2020-01-16 11:12:58
举报
文章被收录于专栏:python3python3

1 引言

最近在开发一个应用软件,为方便调试和后期维护,在代码中添加了日志,用的是Python内置的logging模块,看了许多博主的博文,颇有所得。不得不说,有许多博主大牛总结得确实很好。似乎我再写关于logging的博文有些多余,但不写总结又总觉得没掌握。那就写写吧,也方便日后回顾。

开始总结之前,先感谢几位博主,他们的博客写得很是详尽:

云游道士的博文:https://www.cnblogs.com/yyds/p/6901864.html

Nicholas的博文:https://www.cnblogs.com/Nicholas0707/p/9021672.html

说说为什么需要添加日志?

就像上面说的,为了调试和后期维护方便。也许在开发中没有太大体会,但是如果将软件部署到了生产环境中,一旦出现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的日志记录

现在可以来尝试使用一下上面的函数了:

代码语言:javascript
复制
import logging
 
logging.debug('debug')
logging.info('info')
logging.warn('warn')
logging.error('error')
logging.critical('critical')
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进行一次性配置

尝试使用一下这个配置函数:

代码语言:javascript
复制
import logging
logging.basicConfig(level=logging.DEBUG) # 设置日志等级
logging.debug('debug')
logging.info('info')
logging.warn('warn')
logging.error('error')
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进行配置)时起作用,后面在此设置其他参数是不会生效的。通过代码证明一下:

代码语言:javascript
复制
import logging
logging.basicConfig(level=logging.DEBUG) # 设置日志等级
logging.basicConfig(level=logging.INFO) # 重新设置日志等级
logging.debug('debug')
logging.info('info')
logging.warn('warn')
logging.error('error')
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文件中。代码如下:

代码语言:javascript
复制
import logging
fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S %a'
logging.basicConfig(level=logging.DEBUG,
format=fmt,
datefmt=datefmt,
filename=".log")
logging.debug('debug')
logging.info('info')
logging.warn('warn')
logging.error('error')
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):

代码语言:javascript
复制
import logging
 
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
 
# 控制台输出
con_handler = logging.StreamHandler()
con_handler.setLevel(logging.INFO)
logger.addHandler(con_handler)
 
# 输出到文件a.log
file_a_handler = logging.FileHandler('./a.log', encoding='UTF-8')
file_a_handler.setLevel(logging.DEBUG)
logger.addHandler(file_a_handler)
 
# 输出到文件b.log
file_b_handler = logging.FileHandler('./b.log', encoding='UTF-8')
file_b_handler.setLevel(logging.WARNING)
logger.addHandler(file_b_handler)
 
if __name__=='__main__':
logger.debug('debug msg')
logger.info('info msg')
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参数的配制方法。

例:每条日志输出日志时间、日制定及、所在模块名、函数名、行号等信息,并指定时间输出格式,最后把日志输出到控制台。代码如下:

代码语言:javascript
复制
import logging
 
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
logger.setLevel(logging.DEBUG)
# 定义格式器,添加到处理器中
fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S %a'
log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
handler.setFormatter(log_fmt)
 
logger.addHandler(handler)
 
logger.debug('debug msg')
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则允许输出,反之不允许输出。例如过滤包含敏感信息的日志,过滤器定义如下:

代码语言:javascript
复制
import logging
 
class CountryFilter(logging.Filter):
def filter(self,record):
return "America" not in record.getMessage()
 
logger = logging.getLogger()
handler = logging.StreamHandler()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.addFilter(CountryFilter())
 
logger.critical('I love America')
logger.debug('I love China')

输出结果:

I love China

可以看到,虽然第一条日志记录的日志等级更高,但是因为包含了过滤器中包含的敏感信息,所以不被允许输出。

 4 logging奇淫巧技

4.1 记录异常信息:捕获traceback

如果在日志中,只是记录发生了异常,那其实作用不大,如果traceback也记录到日志中,那就完美了。强大的logging确实也提供了这一功能,而且使用也很简单:

代码语言:javascript
复制
import logging
 
logger = logging.getLogger(__name__)
handler = logging.FileHandler('./.log',encoding='utf-8')
logger.setLevel(logging.DEBUG)
# 定义格式器,添加到处理器中
fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S %a'
log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
handler.setFormatter(log_fmt)
 
logger.addHandler(handler)
 
try:
logger.info('Running …')
1/0
except Exception as e:
logger.error('Exception occurs!',exc_info = True)
# 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中的代码:

代码语言:javascript
复制
import logging
import log_child
logger = logging.getLogger('main')
logger.setLevel(logging.DEBUG)
 
fmt = '%(name)s , %(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S %a'
log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
 
handler = logging.FileHandler('./.log',encoding='utf-8')
handler.setFormatter(log_fmt)
 
logger.addHandler(handler)
 
if __name__=='__main__':
logger.debug('Running …')
log_child.fun_child()

模块child_log.py中的代码:

代码语言:javascript
复制
import logging
 
logger = logging.getLogger('main.child')
logger.setLevel(logging.DEBUG)
def fun_child():
try:
    logger.info('Running …')
    1 / 0
except Exception as e:
    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如下:

代码语言:javascript
复制
[loggers] #固定写法,定义logger的模块
keys=root,log_1,log_2 #创建三个logger,root是父类,必需存在的,其他两个logger的name分别为
 
[logger_root] # 定制上面的logger,严格要求格式为"logger_loggername",必须通过loggername与上面的logger一一对应
level=DEBUG # 设置日志级别
qualname=root # 对于root,其实这里可以不填,默认就是root
handlers=debugs #设置指定处理器,如果有多个处理器,中间以逗号分隔,这个名字待会儿 我们会以固定格式"handler_(value)"创建
 
[logger_log_1]
level=INFO
qualname=log_1 #除了root以外,必须要设置这个属性,用于定义打印输出时候的logger名
handlers=infos
propagate=0 # 是否将消息想父日志传递,0表示不传递,1表示传递。如果向上传递,父日志器接收到消息后会以父日志器的配置再次处理该消息,所以可能所有多次输出
 
[logger_log_2]
level=WARNING
qualname=log_2
handlers=warns
 
[handlers] #固定格式, 开始定义处理器
keys=debugs,infos,warns#定义过滤器名称,与上面出现的handlers的值一一对应,下面定义以handler_handlername格式定义
 
[handler_debugs]
class=StreamHandler # 指定处理器的类名
level=DEBUG # 设置级别
formatter=form01 #定义格式器,名称为form01,下面会创建formatters,格式也是严格要求为formatter_formattername
args=(sys.stdout,) # 控制台输出
 
[handler_infos]
class=FileHandler
level=INFO
formatter=form02
args=('b.log','a')
 
[handler_warns]
class=FileHandler
level=WARNING
formatter=form02
args=('a.log','a')# 写入到文件,写入方式
 
[formatters] #固定格式
keys=form01,form02 #定义名称,下面会引用格式,与上面出现的formatter的值对应
 
[formatter_form01]
format=%(asctime)s %(message)s # 定义消息输出格式,内容
datefmt=%Y-%m-%d %H:%M:%S #日期输出格式
 
[formatter_form02]
format=%(asctime)s %(filename)s %(levelname)s %(message)s
datefmt=%Y-%m-%d %H:%M:%S

实例化logger:

代码语言:javascript
复制
# _*_coding:utf-8_*_
import logging
from logging.config import fileConfig
 
fileConfig('log.conf')
root= logging.getLogger(name="root")
log_1= logging.getLogger(name="log_1")
log_2= logging.getLogger(name="log_2")
 
root.debug('root_debug')
root.info('root_info')
root.warning('root_warning')
log_1.debug('log_1_debug')
log_1.info('log_1_info')
log_1.warning('log_1_warning')
log_2.debug('log_2_debug')
log_2.info('log_2_info')
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:

代码语言:javascript
复制
# -*- coding:utf-8 -*-
import logging
from logging.handlers import RotatingFileHandler
 
logger = logging.getLogger('main')
logger.setLevel(level = logging.INFO)
# 定义一个RotatingFileHandler,最多备份三个日志文件, 每个日志文件最大1k
file_handler = RotatingFileHandler(".log",maxBytes = 1*1024,backupCount = 3)
 
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
 
cons_handler = logging.StreamHandler()
cons_handler.setLevel(logging.DEBUG)
cons_handler.setFormatter(formatter)
 
logger.addHandler(file_handler)
logger.addHandler(cons_handler)
 
if __name__=='__main__':
    while True:
        logger.debug("debug")
        logger.info("info")
        logger.warning("warning")
        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的时间后

代码语言:javascript
复制
import time
import logging
import logging.handlers
 
# logging初始化工作
logging.basicConfig()
 
# logger的初始化工作
logger = logging.getLogger('main')
logger.setLevel(logging.INFO)
 
# 添加TimedRotatingFileHandler
# 定义一个1秒换一次log文件的handler
# 保留3个旧log文件
timefilehandler = logging.handlers.TimedRotatingFileHandler(".log", when='S', interval=1, backupCount=3)
# 设置后缀名称,跟strftime的格式一样
timefilehandler.suffix = "%Y-%m-%d_%H-%M-%S.log"
 
formatter = logging.Formatter('%(asctime)s|%(name)-12s: %(levelname)-8s %(message)s')
timefilehandler.setFormatter(formatter)
logger.addHandler(timefilehandler)
 
while True:
    time.sleep(0.1)
    logger.debug("debug")
    logger.info("info")
    logger.warning("warning")
    logger.critical("critical")

5 总结

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

参考:

https://www.cnblogs.com/yyds/p/6901864.html

https://blog.csdn.net/leiwuhen92/article/details/87914449

http://www.cnblogs.com/andy9468/p/8378492.html

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 引言
  • 2 日志等级
  • 3 记录日志
    • 3.1 记录日志之logging模块级函数
      • 3.2 记录日志之logging四大组件
      •  4 logging奇淫巧技
        • 4.1 记录异常信息:捕获traceback
          • 4.2 多模块共享日志
            • 4.3 使用配置文件配置logger
              • 4.3 日志回滚
              • 5 总结
              相关产品与服务
              日志服务
              日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档