一、logging模块介绍
前言:我们之前运行代码时都是将日志直接输出到控制台,而实际项目中常常需要把日志存储到文件,便于查阅,如运行时间、描述信息以及错误或者异常发生时候的特定上下文信息。
python中自带的logging模块提供了标准的日志接口,在debug时使用往往会事半功倍。为什么不直接使用print去输出呢?这种方式对简单的脚本来说有用,对于复杂的系统来说相当于一个花瓶摆设,大量的print输出很容易被遗忘在代码里,并且print是标准输出,这很难从一堆信息里去判断哪些是你需要重点关注的。
logging的优势就在于可以控制日志的级别,把不需要的信息进行过滤,且可以决定它输出到什么地方、如何输出,还可以通过控制等级把特定等级的信息输出到特定的位置等。logging一共分为四个部分:
- loggers:日志收集器,可供程序直接调用的接口,app通过调用提供的api来记录日志
- handlers:日志处理器, 决定将日志记录分配至正确的目的地
- filters:日志过滤器,对日志信息进行过滤, 提供更细粒度的日志是否输出的判断
- formatters:日志格式器,制定最终记录打印的格式布局
二、日志等级
logging将logger的等级划分成5个level,由低到高分别是debug、info、warning、erroe、critical,默认是warning级别,critical最高,相关等级说明如下:
level | 说明 |
---|---|
debug | 输出详细的运行信息,主要用于调试,追踪问题时使用 |
info | 输出正常的运行的信息,一切按预期进行的情况 |
warning | 一些意想不到的或即将会发生的情况,比如警告:内存空间不足,但不影响程序运行 |
error | 由于某些问题,程序的一些功能会受到影响,还可以继续运行 |
critical | 一个严重的错误,表明程序本身可能无法继续运行 |
这些等级的日志中低包含高,比如info,会收集info及以上等级的日志,debug等级的日志将不进行收集。下面我们来输出这5个等级的日志:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import logging """ logging模块默认收集的日志是warning以上等级的 """ a = 100 logging.debug(a) logging.info( '这是info等级的信息' ) logging.warning( '这是warning等级的信息' ) logging.error( '这是error等级的信息' ) logging.critical( '这是critical等级的信息' ) |
输出结果:
c:\software\python\python.exe d:/learn/test.py
warning:root:这是warning等级的信息
error:root:这是error等级的信息
critical:root:这是critical等级的信息
process finished with exit code 0
三、日志收集器
日志是怎么被收集和输出的呢?答案就是日志收集器,设置一个收集器,把指等级的日志信息输出到指定的地方,控制台或文件等,其工作过程大致如下:
logging中默认的日志收集器是root,收集等级默认是warning,我们可以通过setlevel来改变它的收集等级。
1
2
3
4
5
6
7
8
9
10
11
|
# 获取默认的日志收集器root my_log = logging.getlogger() # 设置默认的日志收集器等级 my_log.setlevel( "debug" ) # 日志将全部被收集 a = 100 logging.debug(a) logging.info( '这是info等级的信息' ) logging.warning( '这是warning等级的信息' ) logging.error( '这是error等级的信息' ) logging.critical( '这是critical等级的信息' ) |
输出结果:
c:\software\python\python.exe d:/learn/test.py
debug:root:100
info:root:这是info等级的信息
warning:root:这是warning等级的信息
error:root:这是error等级的信息
critical:root:这是critical等级的信息
process finished with exit code 0
除了使用默认的日志收集器,我们也可以自己创建一个收集器logging.getlogger,如下:
1
2
3
4
5
6
7
8
9
10
11
|
import logging my_logger = logging.getlogger( 'my_logger' ) # 创建logging对象 my_logger.setlevel( 'info' ) # 设置日志收集等级 a = 100 logging.debug(a) logging.info( '这是info等级的信息' ) logging.warning( '这是warning等级的信息' ) logging.error( '这是error等级的信息' ) logging.critical( '这是critical等级的信息' ) |
输出结果:
c:\software\python\python.exe d:/learn/test.py
warning:root:这是warning等级的信息
error:root:这是error等级的信息
critical:root:这是critical等级的信息
process finished with exit code 0
四、日志处理器
上面例子中设置的收集器都是输出到控制台,除此我们还可以输出到文件中。
handlers(处理器)的作用就是将logger发过来的信息进行准确地分配,送往正确的地方。比如,送往控制台、文件或者是两者。它决定了每个日志收集器的行为,是创建收集器之后需要配置的重点区域。每个handler同样有一个日志级别,一个logger可以拥有多个handler也就是说logger可以根据不同的日志级别将日志传递给不同的handler。当然也可以相同的级别传递给多个handler,这就根据需求来灵活的配置了。
下面实例中设置了两个handler,一个是输出到控制台,一个是输出到文件中。关键代码:
-
logging.streamhandler
:输出到控制台的处理器 -
logging.filehandler
:输出到文件的处理器 -
addhandler
:添加处理器 -
removehandler
:移除处理器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import logging my_logger = logging.getlogger( 'my_logger' ) my_logger.setlevel( 'info' ) # 创建一个输出到控制台的处理器 sh = logging.streamhandler() sh.setlevel( "error" ) # 设置处理器的输出等级 my_logger.addhandler(sh) # 将处理器绑定到日志收集器上 # 创建一个输出到文件的处理器 fh = logging.filehandler( "logs.logs" , encoding = "utf8" ) fh.setlevel( "info" ) my_logger.addhandler(fh) # my_logger.removehandler(fh) # 移除处理器 a = 100 my_logger.debug(a) my_logger.info( '这是info等级的信息' ) my_logger.warning( '这是warning等级的信息' ) my_logger.error( '这是error等级的信息' ) my_logger.critical( '这是critical等级的信息' ) |
运行结果:
c:\software\python\python.exe d:/learn/test.py
这是error等级的信息
这是critical等级的信息
process finished with exit code 0
五、日志过滤器
filters可以实现比level更复杂的过滤功能,限制只有满足过滤规则的日志才会被输出。比如我们定义了filter = logging.filter('a.b')
,并将这个filter添加到了一个handler上,则使用该handler的logger中只有名字带a.b前缀的logger才能输出其日志。下面是一个简单实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import logging # 这是logger1 my_logger = logging.getlogger( 'a.c,b' ) my_logger.setlevel( 'info' ) # 这是logger2 my_logger2 = logging.getlogger( 'a.b' ) my_logger2.setlevel( 'info' ) # 创建一个处理器,两个logger都使用这个处理器 sh = logging.streamhandler() sh.setlevel( "error" ) my_logger.addhandler(sh) my_logger2.addhandler(sh) # 创建一个过滤器绑到处理器上 my_filter = logging. filter (name = 'a.b' ) sh.addfilter(my_filter) # 把过滤器添加到处理器上 # sh2.removefilter(my_filter) # 移除过滤器 my_logger.debug( '这是logger1-debug等级的信息' ) my_logger.info( '这是logger1-info等级的信息' ) my_logger.warning( '这是logger1-warning等级的信息' ) my_logger.error( '这是logger1-error等级的信息' ) my_logger.critical( '这是logger1-critical等级的信息' ) my_logger2.debug( '这是logger2-debug等级的信息' ) my_logger2.info( '这是logger2-info等级的信息' ) my_logger2.warning( '这是logger2-warning等级的信息' ) my_logger2.error( '这是logger2-error等级的信息' ) my_logger2.critical( '这是logger2-critical等级的信息' ) |
因为只有logger2满足过滤器的条件,因此只会输出logger2的日志,运行结果如下:
c:\software\python\python.exe d:/learn/test.py
这是logger2-error等级的信息
这是logger2-critical等级的信息
process finished with exit code 0
filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,非0表示可以通过过滤。
六、日志格式器
顾名思义,对日志进行格式化,因为常规的日志输出并不直观美观,通过美化日志的输出格式,可以让我们阅读起来更加舒服。
format常用格式如下:
-
%(name)s
: 打印收集器名称 -
%(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
: 打印日志信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import logging my_logger = logging.getlogger( 'a.c,b' ) my_logger.setlevel( 'info' ) # 创建一个处理器 sh = logging.streamhandler() sh.setlevel( "error" ) my_logger.addhandler(sh) # 设置一个格式,并设置到处理器上 formatter = logging.formatter( '%(asctime)s - [%(filename)s-->line:%(lineno)d] - %(levelname)s: %(message)s' ) sh.setformatter(formatter) my_logger.debug( '这是logger1-debug等级的信息' ) my_logger.info( '这是logger1-info等级的信息' ) my_logger.warning( '这是logger1-warning等级的信息' ) my_logger.error( '这是logger1-error等级的信息' ) my_logger.critical( '这是logger1-critical等级的信息' ) |
运行结果:
c:\software\python\python.exe d:/learn/test.py
2020-08-01 18:28:43,645 - [test.py-->line:17] - error: 这是logger1-error等级的信息
2020-08-01 18:28:43,645 - [test.py-->line:18] - critical: 这是logger1-critical等级的信息
process finished with exit code 0
七、日志滚动
如果你用 filehandler 存储日志,文件的大小会随着时间推移而不断增大,最终有一天它会占满你所有的磁盘空间。python 的 logging 模块提供了两个支持日志滚动的 filehandler
类,分别是 rotatingfilehandler 和 timedrotatingfilehandler,它就可以解决这个尴尬的问题。
- rotatingfilehandler 的滚动时刻是日志文件的大小达到一定值,当达到指定值的时候,rotatingfilehandler会将日志文件重命名存档,然后打开一个新的日志文件。
- timedrotatingfilehandler 是当某个时刻到来时就进行滚动,同 rotatingfilehandler 一样,当滚动时机来临时,timedrotatingfilehandler 会将日志文件重命名存档,然后打开一个新的日志文件。
在实际应用中,我们通常会根据时间进行滚动,以下实例也以时间滚动为例,按大小滚动的同理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import logging from logging.handlers import timedrotatingfilehandler my_logger = logging.getlogger( 'a.c,b' ) my_logger.setlevel( 'info' ) # 创建一个处理器,使用时间滚动的文件处理器 log_file_handler = timedrotatingfilehandler(filename = 'log.log' , when = 'd' , interval = 1 , backupcount = 10 ) # log_file_handler.suffix = "%y-%m-%d" # log_file_handler.extmatch = re.compile(r"^\d{4}-\d{2}-\d{2}.log$") log_file_handler.setlevel( "error" ) my_logger.addhandler(log_file_handler) # 设置一个格式,并设置到处理器上 formatter = logging.formatter( '%(asctime)s - [%(filename)s-->line:%(lineno)d] - %(levelname)s: %(message)s' ) log_file_handler.setformatter(formatter) my_logger.debug( '这是logger1-debug等级的信息' ) my_logger.info( '这是logger1-info等级的信息' ) my_logger.warning( '这是logger1-warning等级的信息' ) my_logger.error( '这是logger1-error等级的信息' ) my_logger.critical( '这是logger1-critical等级的信息' ) |
参数说明:
filename
:日志文件名;
when
:是一个字符串,用于描述滚动周期的基本单位,字符串的值及意义如下:
- s - seconds
- m - minutes
- h - hours
- d - days
- midnight - roll over at midnight
- w{0-6} - roll over on a certain day; 0 - monday
interval
: 滚动周期,单位由when指定,比如:when='d',interval=1,表示每天产生一个日志文件;
backupcount
: 表示日志文件的保留个数;
除了上述参数之外,timedrotatingfilehandler还有两个比较重要的成员变量,它们分别是suffix和extmatch。suffix是指日志文件名的后缀,suffix中通常带有格式化的时间字符串,filename和suffix由“.”连接构成文件名(例如:filename="test", suffix="%y-%m-%d.log",生成的文件名为test.2020-08-01.log。extmatch是一个编译好的正则表达式,用于匹配日志文件名的后缀,它必须和suffix是匹配的,如果suffix和extmatch匹配不上的话,过期的日志是不会被删除的。比如,suffix=“%y-%m-%d.log”, extmatch的只能是re.compile(r"^\d{4}-\d{2}-\d{2}.log$")。默认情况下,在timedrotatingfilehandler对象初始化时,suffxi和extmatch会根据when的值进行初始化:
1
2
3
4
5
6
|
s:suffix = "%y-%m-%d_%h-%m-%s" ,extmatch = r "\^d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}" ; m:suffix = "%y-%m-%d_%h-%m" ,extmatch = r "^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}" ; h:suffix = "%y-%m-%d_%h" ,extmatch = r "^\d{4}-\d{2}-\d{2}_\d{2}" ; d:suffxi = "%y-%m-%d" ,extmatch = r "^\d{4}-\d{2}-\d{2}" ; midnight: "%y-%m-%d" ,extmatch = r "^\d{4}-\d{2}-\d{2}" ; w: "%y-%m-%d" ,extmatch = r "^\d{4}-\d{2}-\d{2}" ; |
如果对日志文件名没有特殊要求的话,可以不用设置suffix和extmatch,如果需要,一定要让它们匹配上。
八、模块封装
一次封装,一劳永逸,之后直接调用即可,封装内容按需。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import logging from logging.handlers import timedrotatingfilehandler class mylogger( object ): @staticmethod def create_logger(): my_logger = logging.getlogger( "my_logger" ) my_logger.setlevel( "debug" ) # 控制台处理器 stream_handler = logging.streamhandler() stream_handler.setlevel( "error" ) my_logger.addhandler(stream_handler) # 使用时间滚动的文件处理器 log_file_handler = timedrotatingfilehandler(filename = 'log.log' , when = 'd' , interval = 1 , backupcount = 10 ) log_file_handler.setlevel( "info" ) my_logger.addhandler(log_file_handler) formatter = logging.formatter( '%(asctime)s - [%(filename)s-->line:%(lineno)d] - %(levelname)s: %(message)s' ) stream_handler.setformatter(formatter) log_file_handler.setformatter(formatter) return my_logger |
以上就是如何理解python接口自动化之logging日志模块的详细内容,更多关于python 接口自动化 logging日志模块的资料请关注服务器之家其它相关文章!
原文链接:https://www.cnblogs.com/miki-peng/p/13416944.html