赞
踩
转自:https://cloud.tencent.com/developer/article/1354396
1、日志级别
Python 标准库 logging 用作记录日志,默认分为六种日志级别(括号为级别对应的数值),NOTSET(0)、DEBUG(10)、INFO(20)、WARNING(30)、ERROR(40)、CRITICAL(50)。我们自定义日志级别时注意不要和默认的日志级别数值相同,logging 执行时输出大于等于设置的日志级别的日志信息,如设置日志级别是 INFO,则 INFO、WARNING、ERROR、CRITICAL 级别的日志都会输出。
2、logging 流程
官方的 logging 模块工作流程图如下:
从下图中我们可以看出看到这几种 Python 类型,Logger、LogRecord、Filter、Handler、Formatter。
类型说明:
Logger:日志,暴露函数给应用程序,基于日志记录器和过滤器级别决定哪些日志有效。
LogRecord :日志记录器,将日志传到相应的处理器处理。
Handler :处理器, 将(日志记录器产生的)日志记录发送至合适的目的地。
Filter :过滤器, 提供了更好的粒度控制,它可以决定输出哪些日志记录。
Formatter:格式化器, 指明了最终输出中日志记录的布局。
logging流程图.png
3、日志输出格式
日志的输出格式可以认为设置,默认格式为下图所示。
默认日志输出格式.png
4、基本使用
logging 使用非常简单,使用 basicConfig() 方法就能满足基本的使用需要,如果方法没有传入参数,会根据默认的配置创建Logger 对象,默认的日志级别被设置为 WARNING,默认的日志输出格式如上图,该函数可选的参数如下表所示。
参数名称 | 参数描述 |
---|---|
filename | 日志输出到文件的文件名 |
filemode | 文件模式,r[+]、w[+]、a[+] |
format | 日志输出的格式 |
datefat | 日志附带日期时间的格式 |
style | 格式占位符,默认为 "%" 和 “{}” |
level | 设置日志输出级别 |
stream | 定义输出流,用来初始化 StreamHandler 对象,不能 filename 参数一起使用,否则会ValueError 异常 |
handles | 定义处理器,用来创建 Handler 对象,不能和 filename 、stream 参数一起使用,否则也会抛出 ValueError 异常 |
示例代码如下:
- import logging
-
- logging.basicConfig()
- logging.debug('This is a debug message')
- logging.info('This is an info message')
- logging.warning('This is a warning message')
- logging.error('This is an error message')
- logging.critical('This is a critical message')
输出结果如下:
- WARNING:root:This is a warning message
- ERROR:root:This is an error message
- CRITICAL:root:This is a critical message
传入常用的参数,示例代码如下(这里日志格式占位符中的变量放到后面介绍):
- import logging
-
- logging.basicConfig(filename="test.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG)
- logging.debug('This is a debug message')
- logging.info('This is an info message')
- logging.warning('This is a warning message')
- logging.error('This is an error message')
- logging.critical('This is a critical message')
生成的日志文件 test.log ,内容如下:
- 13-10-18 21:10:32 root:DEBUG:This is a debug message
- 13-10-18 21:10:32 root:INFO:This is an info message
- 13-10-18 21:10:32 root:WARNING:This is a warning message
- 13-10-18 21:10:32 root:ERROR:This is an error message
- 13-10-18 21:10:32 root:CRITICAL:This is a critical message
但是当发生异常时,直接使用无参数的 debug()、info()、warning()、error()、critical() 方法并不能记录异常信息,需要设置 exc_info 参数为 True 才可以,或者使用 exception() 方法,还可以使用 log() 方法,但还要设置日志级别和 exc_info 参数。
- import logging
-
- logging.basicConfig(filename="test.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG)
- a = 5
- b = 0
- try:
- c = a / b
- except Exception as e:
- # 下面三种方式三选一,推荐使用第一种
- logging.exception("Exception occurred")
- logging.error("Exception occurred", exc_info=True)
- logging.log(level=logging.DEBUG, msg="Exception occurred", exc_info=True)
5、自定义 Logger
上面的基本使用可以让我们快速上手 logging 模块,但一般并不能满足实际使用,我们还需要自定义 Logger。
一个系统只有一个 Logger 对象,并且该对象不能被直接实例化,没错,这里用到了单例模式,获取 Logger 对象的方法为 getLogger。
注意:这里的单例模式并不是说只有一个 Logger 对象,而是指整个系统只有一个根 Logger 对象,Logger 对象在执行 info()、error() 等方法时实际上调用都是根 Logger 对象对应的 info()、error() 等方法。
我们可以创造多个 Logger 对象,但是真正输出日志的是根 Logger 对象。每个 Logger 对象都可以设置一个名字,如果设置logger = logging.getLogger(__name__)
,__name__ 是 Python 中的一个特殊内置变量,他代表当前模块的名称(默认为 __main__)。则 Logger 对象的 name 为建议使用使用以点号作为分隔符的命名空间等级制度。
Logger 对象可以设置多个 Handler 对象和 Filter 对象,Handler 对象又可以设置 Formatter 对象。Formatter 对象用来设置具体的输出格式,常用变量格式如下表所示,所有参数见 Python(3.7)官方文档:
变量 | 格式 | 变量描述 |
---|---|---|
asctime | %(asctime)s | 将日志的时间构造成可读的形式,默认情况下是精确到毫秒,如 2018-10-13 23:24:57,832,可以额外指定 datefmt 参数来指定该变量的格式 |
name | %(name) | 日志对象的名称 |
filename | %(filename)s | 不包含路径的文件名 |
pathname | %(pathname)s | 包含路径的文件名 |
funcName | %(funcName)s | 日志记录所在的函数名 |
levelname | %(levelname)s | 日志的级别名称 |
message | %(message)s | 具体的日志信息 |
lineno | %(lineno)d | 日志记录所在的行号 |
pathname | %(pathname)s | 完整路径 |
process | %(process)d | 当前进程ID |
processName | %(processName)s | 当前进程名称 |
thread | %(thread)d | 当前线程ID |
threadName | %threadName)s | 当前线程名称 |
Logger 对象和 Handler 对象都可以设置级别,而默认 Logger 对象级别为 30 ,也即 WARNING,默认 Handler 对象级别为 0,也即 NOTSET。logging 模块这样设计是为了更好的灵活性,比如有时候我们既想在控制台中输出DEBUG 级别的日志,又想在文件中输出WARNING级别的日志。可以只设置一个最低级别的 Logger 对象,两个不同级别的 Handler 对象,示例代码如下:
- import logging
- import logging.handlers
-
- logger = logging.getLogger("logger")
-
- handler1 = logging.StreamHandler()
- handler2 = logging.FileHandler(filename="test.log")
-
- logger.setLevel(logging.DEBUG)
- handler1.setLevel(logging.WARNING)
- handler2.setLevel(logging.DEBUG)
-
- formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s")
- handler1.setFormatter(formatter)
- handler2.setFormatter(formatter)
-
- logger.addHandler(handler1)
- logger.addHandler(handler2)
-
- # 分别为 10、30、30
- # print(handler1.level)
- # print(handler2.level)
- # print(logger.level)
-
- logger.debug('This is a customer debug message')
- logger.info('This is an customer info message')
- logger.warning('This is a customer warning message')
- logger.error('This is an customer error message')
- logger.critical('This is a customer critical message')
控制台输出结果为:
- 2018-10-13 23:24:57,832 logger WARNING This is a customer warning message
- 2018-10-13 23:24:57,832 logger ERROR This is an customer error message
- 2018-10-13 23:24:57,832 logger CRITICAL This is a customer critical message
文件中输出内容为:
- 2018-10-13 23:44:59,817 logger DEBUG This is a customer debug message
- 2018-10-13 23:44:59,817 logger INFO This is an customer info message
- 2018-10-13 23:44:59,817 logger WARNING This is a customer warning message
- 2018-10-13 23:44:59,817 logger ERROR This is an customer error message
- 2018-10-13 23:44:59,817 logger CRITICAL This is a customer critical message
创建了自定义的 Logger 对象,就不要在用 logging 中的日志输出方法了,这些方法使用的是默认配置的 Logger 对象,否则会输出的日志信息会重复。
- import logging
- import logging.handlers
-
- logger = logging.getLogger("logger")
- handler = logging.StreamHandler()
- handler.setLevel(logging.DEBUG)
- formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s")
- handler.setFormatter(formatter)
- logger.addHandler(handler)
- logger.debug('This is a customer debug message')
- logging.info('This is an customer info message')
- logger.warning('This is a customer warning message')
- logger.error('This is an customer error message')
- logger.critical('This is a customer critical message')
输出结果如下(可以看到日志信息被输出了两遍):
- 2018-10-13 22:21:35,873 logger WARNING This is a customer warning message
- WARNING:logger:This is a customer warning message
- 2018-10-13 22:21:35,873 logger ERROR This is an customer error message
- ERROR:logger:This is an customer error message
- 2018-10-13 22:21:35,873 logger CRITICAL This is a customer critical message
- CRITICAL:logger:This is a customer critical message
说明:在引入有日志输出的 python 文件时,如 import test.py
,在满足大于当前设置的日志级别后就会输出导入文件中的日志。
6、Logger 配置
通过上面的例子,我们知道创建一个 Logger 对象所需的配置了,上面直接硬编码在程序中配置对象,配置还可以从字典类型的对象和配置文件获取。打开 logging.config Python 文件,可以看到其中的配置解析转换函数。
从字典中获取配置信息:
- import logging.config
-
- config = {
- 'version': 1,
- 'formatters': {
- 'simple': {
- 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
- },
- # 其他的 formatter
- },
- 'handlers': {
- 'console': {
- 'class': 'logging.StreamHandler',
- 'level': 'DEBUG',
- 'formatter': 'simple'
- },
- 'file': {
- 'class': 'logging.FileHandler',
- 'filename': 'logging.log',
- 'level': 'DEBUG',
- 'formatter': 'simple'
- },
- # 其他的 handler
- },
- 'loggers':{
- 'StreamLogger': {
- 'handlers': ['console'],
- 'level': 'DEBUG',
- },
- 'FileLogger': {
- # 既有 console Handler,还有 file Handler
- 'handlers': ['console', 'file'],
- 'level': 'DEBUG',
- },
- # 其他的 Logger
- }
- }
-
- logging.config.dictConfig(config)
- StreamLogger = logging.getLogger("StreamLogger")
- FileLogger = logging.getLogger("FileLogger")
- # 省略日志输出
从配置文件中获取配置信息:
常见的配置文件有 ini 格式、yaml 格式、JSON 格式,或者从网络中获取都是可以的,只要有相应的文件解析器解析配置即可,下面只展示了 ini 格式和 yaml 格式的配置。
test.ini 文件
- [loggers]
- keys=root,sampleLogger
-
- [handlers]
- keys=consoleHandler
-
- [formatters]
- keys=sampleFormatter
-
- [logger_root]
- level=DEBUG
- handlers=consoleHandler
-
- [logger_sampleLogger]
- level=DEBUG
- handlers=consoleHandler
- qualname=sampleLogger
- propagate=0
-
- [handler_consoleHandler]
- class=StreamHandler
- level=DEBUG
- formatter=sampleFormatter
- args=(sys.stdout,)
-
- [formatter_sampleFormatter]
- format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
testinit.py 文件
- import logging.config
-
- logging.config.fileConfig(fname='test.ini', disable_existing_loggers=False)
- logger = logging.getLogger("sampleLogger")
- # 省略日志输出
test.yaml 文件
- version: 1
- formatters:
- simple:
- format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
- handlers:
- console:
- class: logging.StreamHandler
- level: DEBUG
- formatter: simple
-
- loggers:
- simpleExample:
- level: DEBUG
- handlers: [console]
- propagate: no
- root:
- level: DEBUG
- handlers: [console]
testyaml.py 文件
- import logging.config
- # 需要安装 pyymal 库
- import yaml
-
- with open('test.yaml', 'r') as f:
- config = yaml.safe_load(f.read())
- logging.config.dictConfig(config)
-
- logger = logging.getLogger("sampleLogger")
- # 省略日志输出
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。