6.14.logging
from toolkit import H
import logging
Windows 10
Python 3.7.7 @ MSC v.1916 64 bit (AMD64)
Latest build date 2020.08.14
logging version: 0.5.1.2
h = H(logging)
d = h.dicts
module
常量
sort_func = lambda k: eval(f"logging.{k}") \
if (isinstance(eval(f"logging.{k}"), int) and eval(f"logging.{k}") != 1) \
else 100
d["attr"].sort(key=sort_func)
print("{0:<20} {1}\n".format("field", "value"))
for constant in d["attr"]:
value = eval(f"logging.{constant}")
print("{0:<20}:{1}".format(constant, value))
field value
NOTSET :0
DEBUG :10
INFO :20
WARN :30
WARNING :30
ERROR :40
CRITICAL :50
FATAL :50
BASIC_FORMAT :%(levelname)s:%(name)s:%(message)s
lastResort :<_StderrHandler stderr (WARNING)>
logMultiprocessing :True
logProcesses :True
logThreads :True
raiseExceptions :True
root :<RootLogger root (WARNING)>
日志级别越高打印的日志越少,默认的级别是WARNING
,意味着只会追踪该级别及以上的事件。notset
等同于debug
。
记录日志的模块函数
Logging 模块提供了两种日志记录方式:
- 使用 Logging 提供的模块级别的函数
- 使用 Logging 日志系统的四大组件
logging.debug('Python debug')
logging.debug('Python debug')
logging.info('Python info')
# logging.warn is deprecated
logging.warning('Python warning')
logging.error('Python Error')
logging.critical('Python critical')
logging.fatal('Python fatal')
logging.log(logging.WARNING, "log function")
try:
1/0
except Exception as e:
logging.exception(e)
日志输出结果:
WARNING:root:Python warning
ERROR:root:Python Error
CRITICAL:root:Python critical
CRITICAL:root:Python fatal
WARNING:root:log function
ERROR:root:division by zero
Traceback (most recent call last):
File "<ipython-input-16-d24461d1cf55>", line 11, in <module>
1/0
ZeroDivisionError: division by zero
设置日志显示级别
通过 logging.basicConfig()
可以设置 root 的日志级别和日志输出格式。
logging.basicConfig() 关键字参数:
注意:
logging.basicConfig()
需要在开头就设置,在中间设置并无作用
关键字 | 描述 |
---|---|
filename | 创建一个 FileHandler,使用指定的文件名,而不是使用 StreamHandler。 |
filemode | 如果指明了文件名,指明打开文件的模式(如果没有指明 filemode,默认为 ‘a’)。 |
format | handler 使用指明的格式化字符串。 |
datefmt | handler 使用指明的格式化字符串。 |
level | 指明根 logger 的级别。 |
stream | 使用指明的流来初始化 StreamHandler。该参数与 ‘filename’ 不兼容,如果两个都有,’stream’ 被忽略。 |
format 格式
格式 | 描述 |
---|---|
%(levelno)s | 打印日志级别的数值 |
%(levelname)s | 打印日志级别名称 |
%(pathname)s | 打印当前执行程序的路径 |
%(filename)s | 打印当前执行程序名称 |
%(funcName)s | 打印日志的当前函数 |
%(lineno)d | 打印日志的当前行号 |
%(asctime)s | 打印日志的时间 |
%(thread)d | 打印线程 ID |
%(threadName)s | 打印线程名称 |
%(process)d | 打印进程 ID |
%(message)s | 打印日志信息 |
import logging
# 打印日志级别
def test():
logging.basicConfig(level=logging.DEBUG)
logging.debug('Python debug')
logging.info('Python info')
logging.warning('Python warning')
logging.error('Python Error')
logging.critical('Python critical')
logging.log(2, 'test')
test()
输出:
DEBUG:root:Python debug
INFO:root:Python info
WARNING:root:Python warning
ERROR:root:Python Error
CRITICAL:root:Python critical
将日志信息记录到文件
# 日志信息记录到文件
logging.basicConfig(filename='example.log', level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
在相应的路径下会有 example.log 日志文件,内容如下:
DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
多个模块记录日志信息
如果程序包含多个模块,则用以下实例来显示日志信息: 实例中有两个模块,一个模块通过导入另一个模块的方式用日志显示另一个模块的信息:
myapp.py 模块
import logging
import mylib
def main():
logging.basicConfig(filename='myapp.log',level=logging.DEBUG)
logging.info('Started')
mylib.do_something()
logging.info('Finished')
if __name__ == '__main__':
main()
mylib.py 模块
import logging
def do_something():
logging.info('Doing something')
执行 myapp.py 模块会打印相应日志,在文件 myapp.log 中显示信息如下:
INFO:root:Started
INFO:root:Doing something
INFO:root:Finishe
logging 模块四大组件
组件名称 | 对应类名 | 功能描述 |
---|---|---|
日志器 | Logger | 暴露函数给应用程序,基于日志记录器和过滤器级别决定哪些日志有效 |
处理器 | Handler | 将 logger 创建的日志记录发送到合适的目的输出 |
过滤器 | Filter | 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录 |
格式器 | Formatter | 决定日志记录的最终输出格式 |
日志器 - Logger
Logger 持有日志记录器的方法,应用程序代码通过Logger对象调用日志接口,日志记录器不直接实例化,而是通过模块级函数logging.getLogger(name)
来实例化。
logger = logging.getLogger(logger_name)
getLogger()
函数最好加上日志所记录的模块的名字,配置文件和打印日志格式中的%(name)s
对应的是这里的模块名字,如果不指定name则返回root对象。
使用相同的名称多次调用 getLogger()
总是会返回对相同 Logger 对象的引用。
Logger最常用的操作有两类:配置和发送日志消息。
logger = logging.getLogger(__name__)
# 设置日志级别
logger.setLevel(logging.DEBUG)
# 为 Logger 实例增加一个处理器
logger.addHandler(handler_name)
# 为 Logger 实例删除一个处理器
logger.removeHandler(handler_name)
处理器 - Handler
Handler 处理器类型有很多种,比较常用的有三个
StreamHandler
FileHandler
NullHandler
StreamHandler
创建StreamHandler
之后,可以通过使用以下方法设置日志级别,设置格式化器Formatter
,增加或删除过滤器Filter
:
# 创建 StreamHandler 实例
sh = logging.StreamHandler(stream=None)
# 指定日志级别,低于WARN级别的日志将被忽略
sh.setLevel(logging.WARN)
# 设置一个格式化器formatter
sh.setFormatter(formatter_name)
# 增加一个过滤器,可以增加多个
sh.addFilter(filter_name)
# 删除一个过滤器
ch.removeFilter(filter_name)
过滤器 - Filter
Handlers 和 Loggers 可以使用 Filters 来完成比级别更复杂的过滤。 Filter 基类只允许特定 Logger 层次以下的事件。 例如用 ‘A.B’ 初始化的 Filter 允许Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’ 等记录的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字符串来初始化,所有的事件都接受。
filter = logging.Filter(name='')
格式器 - Formatter
使用Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S
。
formatter = logging.Formatter(fmt=None, datefmt=None)
其中,fmt
是消息的格式化字符串,datefmt
是日期字符串。如果不指明datefmt
,将使用‘%(message)s’
。如果不指明datefmt
,将使用 ISO8601 日期格式。
组件之间的关系
- 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,不同的处理器(handler)可以将日志输出到不同的位置。
- 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置。
- 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志。
-
每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。
-
一个 Logger 对象可以包含一个或多个 Handler 和 Filter。
- 一个 Handler 对象可以新增多个格式化器或多个过滤器,而且日志级别将会继承。
配置 logging
虽然我们可以直接调用 Logger、Handler、Filter、Formatter 对象中的方法在代码中来显式配置logging,但这种硬编码的方式不是好的编程规范。在 Python2.7 之后,可以从一个字典加载 logging 配置,这意味着可以从 JSON 或 YAML 文件中加载 logging 配置。虽然也可以通过 old.ini 方式加载 logging 配置,但是这对于可读性和可编辑来说是困难的。从最佳实践的角度来说,logging 模块支持三种配置方式:
logging.config
模块负责配置logging。
-
dictConfig
:通过一个字典进行配置 -
fileConfig
:通过一个文件进行配置 -
listen
:监听一个网络端口,通过接收网络数据来进行配置。
FileConfig
logging.config
模块提供了logging.config.fileConfig()
方法可以从配置文件中进行日志配置。
ini 文件
先创建一个配置文件 logconfig.ini
,配置文件的格式具有如下的形式:
[loggers]
keys=root
[handlers]
keys=fileHandler, errorFileHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=fileHandler, errorFileHandler
[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=simpleFormatter
args=('example.log', 'a')
[handler_errorFileHandler]
class=FileHandler
level=ERROR
formatter=simpleFormatter
args=('example-error.log', 'a')
[formatter_simpleFormatter]
format=%(name)s - %(levelname)s - %(message)s
[loggers]
配置项指定需要配置的logger,root
是logging系统默认logger。若有多个值,用逗号分隔keys=root, testLogger
。然后在[logger_%(name)s]
配置项配置各个logger的具体配置信息。
程序调用配置文件:
import logging
import logging.config
# 文件的后缀名似乎不必须为.ini
logging.config.fileConfig('logconfig.ini')
# create logger
logger = logging.getLogger('simpleExample')
# application code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')
YAML 文件
version: 1
disable_existing_loggers: False
formatters:
simple:
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: simple
stream: ext://sys.stdout
info_file_handler:
class: logging.handlers.RotatingFileHandler
level: INFO
formatter: simple
filename: info.log
maxBytes: 10485760 # 10MB
backupCount: 20
encoding: utf8
error_file_handler:
class: logging.handlers.RotatingFileHandler
level: ERROR
formatter: simple
filename: errors.log
maxBytes: 10485760 # 10MB
backupCount: 20
encoding: utf8
loggers:
my_module:
level: ERROR
handlers: [console]
propagate: no
root:
level: INFO
handlers: [console, info_file_handler, error_file_handler]
JSON 文件
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"simple": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "DEBUG",
"formatter": "simple",
"stream": "ext://sys.stdout"
},
"info_file_handler": {
"class": "logging.handlers.RotatingFileHandler",
"level": "INFO",
"formatter": "simple",
"filename": "info.log",
"maxBytes": 10485760,
"backupCount": 20,
"encoding": "utf8"
},
"error_file_handler": {
"class": "logging.handlers.RotatingFileHandler",
"level": "ERROR",
"formatter": "simple",
"filename": "errors.log",
"maxBytes": 10485760,
"backupCount": 20,
"encoding": "utf8"
}
},
"loggers": {
"my_module": {
"level": "ERROR",
"handlers": ["console"],
"propagate": "no"
}
},
"root": {
"level": "INFO",
"handlers": ["console", "info_file_handler", "error_file_handler"]
}
}
dictConfig
同时向控制台和文件输出日志。
import logging
from logging.config import dictConfig
level = logging.DEBUG
logging_config = {
'version': 1,
'formatters': {
'default':
{'format': '%(asctime)s [%(process)d] [%(levelname)s] %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'}
},
'handlers': {
'console': {
'level': level,
'class': 'logging.StreamHandler',
'formatter': 'default',
},
'file': {
'level': level,
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'default',
'filename': './log/mtwoai.log',
'maxBytes': 1024 * 1024 * 10,
'backupCount': 1
}
},
'loggers': {
'test': {
'level': level,
'handlers': ['console', 'file']
}
},
'root': {
'level': level,
'handlers': ['console', 'file']
},
'disable_existing_loggers': False
}
dictConfig(logging_config)
在python代码中配置
import logging
logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.debug('often makes a very good meal of %s', 'visiting tourists')