python的logging模块使用方法

logging模块简介

logging模块Python内置的日志模块,用来生成程序的日志。一条日志对应一个事件的发生,一个事件一包括:事件发生时间、事件发生位置、事件内容、事件严重程度-日志级别。(还可以包括进程ID、进程名称、线程ID、线程名称等)

logging模块的组成

  1. 日志级别:日志分为五个等级,从低到高分别是:DEBUGINFOWANGINGERRORCRITICAL

DEBUG最详细的信息,通常定位问题的时候用

INFO确认一切按照预期运行详细程度仅次于DEBUG

WARNING:一些意想不到的问题发生了,或者不久的将来将要发生问题,比如磁盘空间小,软件还能正常运行。

ERROR:很严重的问题,软件没能正确执行某些功能

CRITICAL:一个严重的错误,程序本无法继续运行了。

      你的程序指定了一个日志级别后,程序记录所有日志级别大于等于指定级别的log比如指定的级别是WARNING,那么记录的将是WARNINGERRORCRITICAL至于如何设置指定日志级别,将在后面说

  1. logging4组件:

logger(日志器):日志类,通过这个类提供的API来记录日志。

handler(处理器):对日志信息处理可以将日志发送到不同的目标域中。

filter(过滤器):对日志信息进行过滤。

formatter(格式器):日志的格式化

logging模块使用

3.1 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的日志记录。

logging.log(level,*args,**kwargs): 创建一条严重级别为level的日志记录。

logging.basicConfig(**kwargs):root logger进行一次性配置。用于指定要记录的日志级别,日志格式,日志输出位置,日志文件打开模式等信息。

举例

logging.warning("This is a warning log")

输出WARNING:root:This is a warning log

或者也可以用:logging.log(logging.WARNING,"This is a warning log")

输出WARNING:root:This is a warning log

 

可以看出来上面两种方式输出结果是一样的,结果三部分组成,第一部分WARNING日志级别,第二部分root是日志器名称,第三部发是具体的日志内容。这是因为logging模块有一个默认的输出格式“%(levelname)s:%(name)s:%(message)s”。没有手动设置格式的情况是就按照这种默认的日志格式。日志器名称默认情况是是root,后面到如何修改日志器名称。

另外要注意一点,如果你现在logging.info("This is a info log"),或者logging. debug("This is a debug log"))你会发现控制台不会打印任何东西出来为什么?也是因为logging模块的默认设置,默认为程序指定日志级别是WARNING。所以只有大于等于这个级别的log会打印出来。

上面提到的所有默认设置都是在logging.basicConfig(**kwargs)中设置,可以查看这个方法的源代码确认。

3.2 logging.basicConfig(**kwargs)方法

这个方法接收的参数是一个字典,字典的键为:

filename:指定日志输出目标文件的文件名,指定该项后日志信息就不会被输出到控制台

filemode:指定日志文件的打开模式,默认为”a”,需要注意的是,选项在指定了filename后才有效。

format指定日志格式字符串即指定日志输出时所包含的字段信息以及它们的顺序。

datefmt指定日期/时间格式,该选项要在format中包含时间字段%(asctime)才有效。

level指定日志器的日志级别。

stream指定日志输出目标stream。如sys.stdoutsys.stderr以及网络streamstreamfilename不能同时提供。否则会引发ValueError异常。

style指定format格式字符串的风格,默认为%可以为:’$’’{’

handlers如果该选项存在,应该是一个创建了多个handlers的可迭代对象这些handlers将会被添加root logger中。该选项stream以及filename只能存在一个。

举例

# 设置日志级别,通过这个设置,就可以记录debug级别和info级别的log

logging.basicConfig(level=logging.DEBUG)   

logging.info("This is a info log")

输出WARNING:root:This is a info log

3.3logging模块定义的格式字符串字段

下面是一些logging模块中定义好的可以用于format格式字符串中的字段

字段/属性名称

使用格式

描述

asctime

%(asctime)s

日志事件发生的时间,比如:2019-12-23 11:04:10 ,050

created

%(created)f

日志事件发生的时间戳,即调用time.time()返回的值

relativeCreated

%(relativeCreated)d

日志事件发生的时间相对于logging模块加载时间的相对毫秒数

msecs

%(msecs)d

日志事件发生时间的毫秒部分

levelname

%(levelname)s

该日志记录的文字形式的日志级别,如DEBUG、INFO

levelno

%(levelno)s

该日志记录的数值形式的日志级别(10,20,30,40,50)

name

%(name)s

使用的日志器名称,默认为root

message

%(message)s

日志记录的文本内容

pathname

%( pathname)s

调用日志记录函数的源码文件的全路径

filename

%( filename)s

pathname的文件名部分,包含文件后缀

module

%(module)s

filename的名称部分,不包含后缀

lineno

%(lineno)d

调用日志记录函数的源代码所在的行号

funcName

%( funcName)s

调用日志记录函数的函数名

process

%( process)d

进程ID

processName

%( processName)s

进程名称

thread

%( thread)d

线程ID

threadName

%( threadName)s

线程名称

举例

 

import logging

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
logging.basicConfig(filename="py.log", level=logging.DEBUG, format=LOG_FORMAT)
logging.debug("This is a debug log")
logging.warning("This is a warning log")

 

 

 

当前路径可以找到一个py.log文件,打开可以看到结果:

2019-12-23 11:23:04,213 - DEBUG - This is a debug log
2019-12-23 11:23:04,213 - WARNING - This is a warning log

日期时间看着有点别扭,可以修改为:

 

import logging

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FORMAT = "%Y%m%d %H:%M:%S %p"

logging.basicConfig(filename="py.log", level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)
logging.debug("This is a debug log")
logging.warning("This is a warning log")

 

 

 

输出结果为:

20191223 11:28:16 AM - DEBUG - This is a debug log
20191223 11:28:16 AM - WARNING - This is a warning log

3.4补充说明

1.logging.basicConfig()一个一次性简单配置工具,只有第一次调用的时候会起作用。

2.如果要记录的日志中包含变量数据,可以使用一个格式字符串作为这个事件的描述信息然后将变量数据作为第二个参数*args的值进行传递,如logging.warning(‘%s is %d years old ’,’Tom’,10)

3.logging.debug()方法中的**kwargs支持3关键字,exc_info,stack_info,extra.

exc_info:为布尔值,如果该参数的值设置为true,则会将异常信息添加到日志信息中。如果没有就添加None到日志信息中

stack_info:布尔值,默认值为False,如果设置为True信息将会被添加到日志信息中。

extra:这是一个字典参数,可以用来定义消息格式中所包含的字段,但是它的key不能logging模块定义的字段冲突。

 

import logging

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s - %(user)s[%(ip)s]"
DATE_FORMAT = "%Y%m%d %H:%M:%S %p"
logging.basicConfig(filename="py.log", level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)
logging.debug("This is a debug log")
logging.warning("This is a warning log", extra={'user': 'Tom', 'ip': '192.168.0.0'})

 

 

 

输出的结果为:

20191223 14:11:18 PM - WARNING - This is a warning log - Tom[192.168.0.0]

 

logging模块的四大组件

四大组件上面说了有日志器、处理器、过滤器和格式器,日志器需要通过处理器将日志信息输出到目标位置,不同的处理器可以将日志输出到不同的位置。日志器可以设置多个处理器同一条日志输出到不同的位置,每一个处理器都可以设置自己的过滤器,实现日志过滤,从而只保留自己需要的日志每一个处理又可以分别设置自己的格式器,实现同一条日志以不同的格式输出不同的位置

4.1 Logger

Logger对象常用的配置方法:

Logger.setLevel():设置日志器将会处理的最低级别日志

Logger.addHandler()Logger.removeHandler():Logger对象添加移除一个Handler对象

Logger.addFilter()Logger.removeFilter():logger对象添加或移除一个filter对象

通过上面的方法配置完成后,就可以创建日志记录了:

Logger.debug(),Logger.info(),Logger.warning(),Logger.error(),Logger.critical():创建一个debuginfowarningerrorcritical等级的日志

Logger.exception():创建一个类似于Logger.error()日志消息。

Logger.log()需要获取一个明确的日志level参数来创建一个日志记录。

说明

Logger.exception()Logger.error()区别在于:Logger.exception()将会输出堆栈追踪信息,另外通常只是在一个exception handler调用该方法

Logger.log()Logger.debug()方法的区别:Logger.log()可以输出自定义level的日志。

 

怎样得到一个Logger对象

一种方式是通过Logger类实例化得到一个事例,另外一种推荐使用的方法是通过调用logging.getLogger()方法,这个方法有一个可选参数叫name表示将要返回的日志器名称,如果不这个参数值,默认名称就是root,若以相同name参数多次调用getLogger()方法,返回的是同一个logger对象引用。

 

关于logger的层级结构与有效等级的说明

  1. logger的名称是一个以’.’分割的层级结构每个’.’后面是前面的children,例如foo.bar以及foo.bar.bam都是foo的后代。
  2. 后代如果没有设置最低level,则就去父level,父代也没有就去祖父代,都没有设置level的情况下默认就是warning
  3. child loggers完成对日志的处理后,就将日志消息传递给祖先loggers相关的handlers所以一般给祖先配置一个handlers足够了也可以通过一个loggerpropagate属性设置为False关闭传递。

4.2 Handlers

Handler对象的作用是将不同level的日志分发到不同的handler指定的位置,比如需求

  1. 所有日志都发送一个日志文件
  2. 所有严重级别大于等于error的日志发送到stdout标准输出)
  3. 所有严重级别为critical的日志发送到一个Email邮件地址

这种情况下就需要三个不同的handlers,每一个handler负责发送一个level级别的日志到指定位置。

常用的方法:

Handler.setLevel() :设置handler将会处理的日志消息的最低严重级别

Handler.serFormatter()设置一个格式器对象

Handler.addFilter()Handler.removeFilter()handler添加和删除一个过滤器对象。

 

应用程序代码不应该直接实例化和使用Handler实例,因为Handler只是一个基类,提供子类重写覆盖的一些接口。

Handler

描述

logging.StreamHandler

将日志消息发送到输出到Stream,如std.out,std.err

logging.FileHandler

将日志消息发送到磁盘文件,默认情况下文件大小会无限增长

logging.handlers.RotatingFileHandler

将日志消息发送到磁盘文件,并支持日志文件按照大小切割

logging.handlers.TimeRotatingFileHandler

将日志消息发送到磁盘文件,并支持日志文件按照时间切割

logging.handlers.HTTPHandler

将日志消息以get或者post方式发送给HTTP服务器

longing.handlers.SMTPHandler

将日志消息发送给指定的Email地址

logging.NullHandler

该Handler实例会忽略error messages

 

4.2 Formater

Formater类用于定义日志输出的格式,内容

logging.Formatter.__init__(fmt=None, datefmt=None,style=’%’)

fmt:指定消息格式化字符串,如果不给值,就默认使用message原始内容

datefmt:指定日期格式字符串

style指定format格式字符串的风格,默认为%可以为:’$’’{’

4.3Filter

可以实现比level更复杂的过滤功能。Filter只允许某logger层级下的日志事件通过过滤

 

定义为

class logging.Filter(name=’’):

filter(record)

比如:一个filter实例化时传递的name参数’A.B’那么该filter实例将允许名称为类似如下规则的loggers产生的日志记录通过过滤‘A.B’‘A.B.C’‘A.B.D’‘A.B.C.D’而名称为‘A.BB’‘B.A.B’loggers产生的日志则会被过滤掉,如果name为空,则允许所有日志通过过滤。filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,0表示可以通过过滤。

 

记录log过滤流程

1、 日志器等级过滤

2、 日志器的过滤器过滤

3、 日志器的处理器等级过滤

4、 日志器的处理器的过滤器过滤

 

4.4使用4组件记录日志举例

需求

记录所有的日志到all.log,格式为:日期和时间-日志级别-日志信息

记录error等级以上的级别的logerror.log,格式为:日期和时间-日志级别-文件名[行号]-日志信息

all.log每天凌晨进行日志切割。

 

 1 import logging.handlers
 2 import datetime
 3 
 4 
 5 logger = logging.getLogger("mylogger")
 6 logger.setLevel(logging.DEBUG)
 7 
 8 # 记录全部日志的句柄,all.log每天凌晨进行日志切割
 9 rf_handler = logging.handlers.TimedRotatingFileHandler('all.log', when='midnight', interval=1, backupCount=7, atTime=datetime.time(0, 0, 0, 0))
10 rf_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
11 
12 # 记录error级别以上的日志句柄
13 f_handler = logging.FileHandler('error.log')
14 f_handler.setLevel(logging.ERROR)
15 f_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s
16 
17 - %(filename)s[:%(lineno)d] - %(message)s'))
18 
19 # 将两个handler处理器分别给日志器,最后会生成两份日志
20 logger.addHandler(rf_handler)
21 logger.addHandler(f_handler)
22 
23 logger.debug("This is a debug log")
24 logger.error("This is a error log")
25 logger.info("This is a info log")
26 
27 # 子孙类日志事例
28 # 定义一个子孙类日志器,子孙类的日志会单独存一份log到error2,同时也会传递到父类的log中
29 logger2 = logging.getLogger('mylogger.son')
30 f_handler2 = logging.FileHandler('error2.log')
31 f_handler2.setLevel(logging.ERROR)
32 f_handler2.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s'))
33 logger2.addHandler(f_handler2)
34 logger2.debug("22222222222")
35 logger2.error("error333333")
36 
37 # 如果不想将子孙类的日志传递到父类中,使用属性propagate
38 logger2.propagate = False
39 logger2.info("444")
40 logger2.error("error555")

另外还可以利用logging.config模块

 1 import logging.config
 2 
 3 
 4 # 通过字典配置日志模块
 5 # 每次都要编写代码来配置非常麻烦,我们可以写一写完整的配置保存起来,以便后续直接使用
 6 
 7 # 格式1 给开发看的
 8 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s] [%(filename)s:%(lineno)d] [%(levelname)s] [%(message)s]'
 9 logfile_path1 = "coder.log"
10 
11 # 格式2 给领导看的简略版
12 simple_format = '[%(levelname)s] [%(asctime)s] [%(message)s]'
13 logfile_path2 = 'boss.log'
14 
15 LOGGING_DIC = {
16     'version': 1,
17     'disable_existing_loggers': False,
18     'formatters': {
19         'standard': {
20             'format': standard_format
21         },
22         'simple': {
23             'format': simple_format
24         },
25     },
26     'filters': {},
27     'handlers': {
28         # 打印到终端的日志
29         'console': {
30             'level': 'DEBUG',
31             'class': 'logging.StreamHandler',
32             'formatter': 'simple'
33         },
34         # 打印到文件的日志,收集 DEBUG以上的日志
35         'std': {
36             'level': 'DEBUG',
37             'class': 'logging.handlers.RotatingFileHandler',
38             'formatter': 'standard',
39             'filename': logfile_path1,
40             'maxBytes': 1024*1024*5,  # 日志大小 5M
41             'backupCount': 5,  # 日志文件最大个数
42             'encoding': 'utf-8'
43         },
44         'boss': {
45             'level': INFO,
46             'class': 'logging.handlers.RotatingFileHandler',
47             'formatter': 'simple',
48             'filename': logfile_path2,
49             'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
50             'backupCount': 5,  # 日志文件最大个数
51             'encoding': 'utf-8'
52         }
53     },
54     'loggers': {
55         # logging.getLogger(__name__)拿到的logger配置
56         'aa': {
57             'handlers': ['std', 'console', 'boss'],
58             'level': DEBUG,
59             'propagate': 'True'
60         },
61     },
62 }
63 
64 logging.config.dictConfig(LOGGING_DIC)
65 logger = logging.getLogger("aa")
66 logger.error("testing error")
67 logger.debug("debug")

 

posted @ 2020-01-07 11:02  白杨的博客  阅读(633)  评论(0编辑  收藏  举报