共计 8822 个字符,预计需要花费 23 分钟才能阅读完成。
为何要应用日志?
- 进行程序 (代码) 的调试
- 程序运行过程中的问题定位和剖析
- 收集程序运行的状况
logging 模块日志事件级别
默认的级别是
WARNING
,意味着只会追踪该级别及以上的事件,除非更改日志配置。所追踪事件能够以不同模式解决。最简略的形式是输入到控制台。另一种罕用的形式是写入磁盘文件。
级别 | 数值 | 何时应用 |
---|---|---|
DEBUG | 10 | 细节信息,仅当诊断问题时实用。 |
INFO | 20 | 确认程序按预期运行 |
WARNING | 30 | 表明有曾经或行将产生的意外(例如:磁盘空间有余)。程序仍按预期进行 |
ERROR | 40 | 因为重大的问题,程序的某些性能曾经不能失常执行 |
CRITICAL | 50 | 重大的谬误,表明程序已不能继续执行 |
# -*- coding:utf-8 -*- | |
import logging | |
# 应用 basicConfig()来指定日志级别和相干信息 | |
logging.basicConfig( | |
level=logging.DEBUG # 设置日志输入级别 | |
,filename="demo.log" # log 日志输入的文件地位和文件名 | |
,filemode="w" # 文件的写入格局,w 为从新写入文件,默认是追加 | |
# 日志输入的格局, - 8 示意占位符,让输入左对齐,输入长度都为 8 位 | |
,format="%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s" | |
,datefmt="%Y-%m-%d %H:%M:%S" # 工夫输入的格局 | |
) | |
# 默认的 warning 级别,只输入 warning 以上的 | |
logging.debug("This is DEBUG !!") | |
logging.info("This is INFO !!") | |
logging.warning("This is WARNING !!") | |
logging.error("This is ERROR !!") | |
logging.critical("This is CRITICAL !!") | |
# 在理论我的项目中,捕捉异样的时候,如果应用 logging.error(e),只提醒指定的 logging 信息 | |
# 不会呈现为什么会错的信息,所以要应用 logging.exception(e)去记录。try: | |
3/0 | |
except Exception as e: | |
# logging.error(e) | |
logging.exception(e) |
日志输入后果如下:
2022-08-01 16:46:18 - root - DEBUG - test.py : 14 line - This is DEBUG !! | |
2022-08-01 16:46:18 - root - INFO - test.py : 15 line - This is INFO !! | |
2022-08-01 16:46:18 - root - WARNING - test.py : 16 line - This is WARNING !! | |
2022-08-01 16:46:18 - root - ERROR - test.py : 17 line - This is ERROR !! | |
2022-08-01 16:46:18 - root - CRITICAL - test.py : 18 line - This is CRITICAL !! | |
2022-08-01 16:46:18 - root - ERROR - test.py : 27 line - division by zero | |
Traceback (most recent call last): | |
File "/Users/yg/Documents/test.py", line 24, in <module> | |
3/0 | |
ZeroDivisionError: division by zero |
日志组件
logging 采纳了模块化设计,次要蕴含四种组件:
组件 | 性能 |
---|---|
Logger | 记录器,提供利用程序代码能间接应用的接口。 |
Handler | 处理器,将记录器产生的日志发送到目的地。(输入到文件或者控制台或者邮件等,一个记录器能够对应多个处理器。) |
Filter | 过滤器,提供更好的粒度管制,决定哪些日志会被输入。 |
Formatter | 格式化器,设置日志内容的组成构造和音讯字段。 |
Loggers 记录器
-
提供应用程序的调用接口
logger = logging.getLogger(__name__) # logger 是单例的,也就是__name__不变,获取到的 logger 都是同一个 -
设置日志记录的级别
logger.setLevel()
-
将日志内容传递到相关联的 handlers 中
logger.addHandler() logger.removeHandler()
Handler 处理器
将日志发送到不同的中央
# 常见的处理器 | |
StreamHandler # 屏幕输入 | |
FileHandler # 文件记录 | |
BaseRotatingHandler # 规范的宰割文件日志 | |
RotatingFileHandler # 按文件大小记录日志 | |
TimeRotatingFileHandler # 按工夫记录日志 |
StreamHandler
规范输入散发器,也就是屏幕显示
sh = logging.StreamHandler(stream=None)
FileHandler
将日志保留到文件中
fh = logging.FileHandler(filename,mode='a',encoding=None,delay=False)
setFormatter()
设置以后 Handler 对象应用的音讯格局
Filter 过滤器
Filter 能够被 Handler 和 Logger 用来做比之前设置的日志等级 level 更为细粒度的、更简单的相干过滤性能。
简略的说 Filter 是一个过滤器基类,它只容许某个 logger 层级下的日志事件通过过滤,保留下来。该类定义如下:
class logging.Filter(name='') | |
filter(record) |
过滤举例:
例如用‘A.B’初始化的 Filter,那么其容许 Logger‘A.B’,‘A.B.C’,‘A.B.C.D’,‘A.B.D’等日志记录的事件,logger‘A.BB’,‘B.A.B’等就不满足过滤的条件。
如果用空字符串来对 Filter 初始化,所有日志记录的事件都将不会被过滤。
Formatter 格式化器
formatter 对象用来最终设置日志信息的程序,构造和内容
ft = logging.Formatter.__init__(fmt=None,datafmt=None)
属性 | 格局 | 形容 |
---|---|---|
asctime | %(asctime)s | 日志产生的工夫,默认格局为 msecs2003-07-08 16:49:45, 896 |
message | %(message)s | 具体的日志信息 |
filename | %(filename)s | 生成日志的程序名 |
name | %(name)s | 日志调用者 |
levelname | %(levelname)s | 日志级別(DEBUG, INFO, WARNING, ERROR, CRITICAL) |
lineno | %(lineno)d | 日志所针对的代码行号(如果可用的话) |
msecs | %(msecs)d | 日志生成工夫的亳秒局部 |
created | %(created)f | time.tme)生成的日志创立工夫戳 |
funcname | %(funcname)s | 调用日志的函数名 |
levene | %(leveling)s | 日志级别对应的数值 |
module | %(module)s | 生成日志的模块名 |
pathname | %(pathname)s | 生成日志的文件的残缺门路 |
process | %(process)d | 生成日志的过程 D(如果可用) |
processname | %(processname)s | 过程名(如果可用) |
thread | %(thread)d | 生成日志的线程 D(如果可用) |
threadname | %(threadname)s | 线程名(如果可用) |
logging 编程形式代码 demo
# -*- coding:utf-8 -*- | |
import logging | |
#编程的形式记录日志 | |
#记录器 | |
logger1 = logging.getLogger("logger1") | |
logger1.setLevel(logging.DEBUG) | |
print(logger1) | |
print(type(logger1)) | |
logger2 = logging.getLogger("logger2") | |
logger2.setLevel(logging.INFO) | |
print(logger2) | |
print(type(logger2)) | |
#处理器 | |
#1. 规范输入 | |
sh1 = logging.StreamHandler() | |
sh1.setLevel(logging.WARNING) | |
sh2 = logging.StreamHandler() | |
# 2. 文件输入 | |
# 没有设置输入级别,将用 logger1 的输入级别(并且输入级别在设置的时候级别不能比 Logger 的低!!!),设置了就应用本人的输入级别 | |
fh1 = logging.FileHandler(filename="fh.log",mode='w') | |
fh2 = logging.FileHandler(filename="fh.log",mode='a') | |
fh2.setLevel(logging.WARNING) | |
# 格局器 | |
fmt1 = logging.Formatter(fmt="%(asctime)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s") | |
fmt2 = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s" | |
,datefmt="%Y/%m/%d %H:%M:%S") | |
#给处理器设置格局 | |
sh1.setFormatter(fmt1) | |
fh1.setFormatter(fmt2) | |
sh2.setFormatter(fmt2) | |
fh2.setFormatter(fmt1) | |
#记录器设置处理器 | |
logger1.addHandler(sh1) | |
logger1.addHandler(fh1) | |
logger2.addHandler(sh2) | |
logger2.addHandler(fh2) | |
#打印日志代码 | |
logger1.debug("This is DEBUG of logger1 !!") | |
logger1.info("This is INFO of logger1 !!") | |
logger1.warning("This is WARNING of logger1 !!") | |
logger1.error("This is ERROR of logger1 !!") | |
logger1.critical("This is CRITICAL of logger1 !!") | |
logger2.debug("This is DEBUG of logger2 !!") | |
logger2.info("This is INFO of logger2 !!") | |
logger2.warning("This is WARNING of logger2 !!") | |
logger2.error("This is ERROR of logger2 !!") | |
logger2.critical("This is CRITICAL of logger2 !!") |
控制台输入:
<Logger logger1 (DEBUG)> | |
<class 'logging.Logger'> | |
<Logger logger2 (INFO)> | |
<class 'logging.Logger'> | |
2022-08-01 18:04:28,160 - WARNING - test.py : 56 line - This is WARNING of logger1 !! | |
2022-08-01 18:04:28,160 - ERROR - test.py : 57 line - This is ERROR of logger1 !! | |
2022-08-01 18:04:28,160 - CRITICAL - test.py : 58 line - This is CRITICAL of logger1 !! | |
2022/08/01 18:04:28 - logger2 - INFO - test.py : 61 line - This is INFO of logger2 !! | |
2022/08/01 18:04:28 - logger2 - WARNING - test.py : 62 line - This is WARNING of logger2 !! | |
2022/08/01 18:04:28 - logger2 - ERROR - test.py : 63 line - This is ERROR of logger2 !! | |
2022/08/01 18:04:28 - logger2 - CRITICAL - test.py : 64 line - This is CRITICAL of logger2 !! |
文件 fh.log 输入:
2022/08/01 18:04:28 - logger1 - DEBUG - test.py : 54 line - This is DEBUG of logger1 !! | |
2022/08/01 18:04:28 - logger1 - INFO - test.py : 55 line - This is INFO of logger1 !! | |
2022/08/01 18:04:28 - logger1 - WARNING - test.py : 56 line - This is WARNING of logger1 !! | |
2022/08/01 18:04:28 - logger1 - ERROR - test.py : 57 line - This is ERROR of logger1 !! | |
2022/08/01 18:04:28 - logger1 - CRITICAL - test.py : 58 line - This is CRITICAL of logger1 !! | |
2022-08-01 18:04:28,160 - WARNING - test.py : 62 line - This is WARNING of logger2 !! | |
2022-08-01 18:04:28,160 - ERROR - test.py : 63 line - This is ERROR of logger2 !! | |
2022-08-01 18:04:28,160 - CRITICAL - test.py : 64 line - This is CRITICAL of logger2 !!``` |
过滤器在代码里的利用
# 定义一个过滤器 | |
flt1 = logging.Filter("logger1") #名字就是 logger 记录器的名字 | |
# 关联过滤器 | |
# 1.logger 关联过滤器 | |
logger.addFilter(flt1) | |
# 2. 处理器关联过滤器 | |
sh1.addFilter(flt1) | |
# fh1.addFilter(flt1) | |
# 过滤器定义在记录器上的时候,不论你设置了任何对于这个过滤器的任何设置,都不起作用。# 过滤器定义在哪个处理器上,哪个处理器就不起作用,也就能够管制日志的输入,想要还是不想要。# 特地是在我的项目调试的时候,不想让日志记录到文件里去,间接在屏幕显示,就过滤到 fh | |
# 代码上线后,日志记录到文件,不必在屏幕显示,就过滤掉 sh |
文件配置 logging 编程
上述的办法都写的很死板,在小工程或者单个文件还能够,然而当我的项目大了,咱们就必须应用配置文件的形式配置 logging 的信息,从而来灵便应用,次要有两种形式去配置,一种是 conf 文件,一种是字典文件。
import logging.config | |
logging.config.fileConfig("/path/to/configfile") | |
logging.config.dictConfig("/path/to/configfile") |
yaml 配置文件:
version: 1 | |
# 是否笼罩掉曾经存在的 loggers | |
disable_existing_loggers: True | |
formatters: | |
tostrout: | |
format: "%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s" | |
datefmt: "%Y/%m/%d %H:%M:%S" | |
tofile: | |
format: "%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s" | |
handlers: | |
sh: | |
class: logging.StreamHandler | |
level: WARNING | |
formatter: tostrout | |
stream: ext://sys.stdout | |
fh: | |
class: logging.handlers.TimedRotatingFileHandler | |
filename: info.log | |
interval: 1 | |
backupCount: 2 | |
when: D | |
level: INFO | |
formatter: tofile | |
loggers: | |
logger1: | |
level: DEBUG | |
handlers: [sh] | |
# 是否往下级 Logger 传递,如果为 yes 的话,root 抉择了两个 logger,这里的日志也会在两个 logger 的配置中输入,会反复。所以选 No, 本人记录本人的日志。propagate: no | |
logger2: | |
level: INFO | |
handlers: [fh] | |
propagate: no | |
root: | |
level: DEBUG | |
handlers: [sh,fh] | |
propagate: no |
调用代码:
# -*- coding:utf-8 -*- | |
import logging | |
import logging.config | |
import yaml | |
with open("logger_config.yaml", "r") as f: | |
dict_conf = yaml.safe_load(f) | |
logging.config.dictConfig(dict_conf) | |
root = logging.getLogger() | |
logger1 = logging.getLogger('logger1') | |
logger2 = logging.getLogger('logger2') | |
# 打印日志代码 | |
root.debug("This is DEBUG of root !!") | |
root.info("This is INFO of root !!") | |
root.warning("This is WARNING of root !!") | |
root.error("This is ERROR of root !!") | |
root.critical("This is CRITICAL of root !!") | |
logger1.debug("This is DEBUG of logger1 !!") | |
logger1.info("This is INFO of logger1 !!") | |
logger1.warning("This is WARNING of logger1 !!") | |
logger1.error("This is ERROR of logger1 !!") | |
logger1.critical("This is CRITICAL of logger1 !!") | |
logger2.debug("This is DEBUG of logger2 !!") | |
logger2.info("This is INFO of logger2 !!") | |
logger2.warning("This is WARNING of logger2 !!") | |
logger2.error("This is ERROR of logger2 !!") | |
logger2.critical("This is CRITICAL of logger2 !!") |
规范输入:
2022/08/02 01:23:05 - root - WARNING - test.py : 17 line - This is WARNING of root !! | |
2022/08/02 01:23:05 - root - ERROR - test.py : 18 line - This is ERROR of root !! | |
2022/08/02 01:23:05 - root - CRITICAL - test.py : 19 line - This is CRITICAL of root !! | |
2022/08/02 01:23:05 - logger1 - WARNING - test.py : 23 line - This is WARNING of logger1 !! | |
2022/08/02 01:23:05 - logger1 - ERROR - test.py : 24 line - This is ERROR of logger1 !! | |
2022/08/02 01:23:05 - logger1 - CRITICAL - test.py : 25 line - This is CRITICAL of logger1 !! |
文件输入(info.log):
2022-08-02 01:23:05,688 - root - INFO - test.py : 16 line - This is INFO of root !! | |
2022-08-02 01:23:05,688 - root - WARNING - test.py : 17 line - This is WARNING of root !! | |
2022-08-02 01:23:05,688 - root - ERROR - test.py : 18 line - This is ERROR of root !! | |
2022-08-02 01:23:05,688 - root - CRITICAL - test.py : 19 line - This is CRITICAL of root !! | |
2022-08-02 01:23:05,688 - logger2 - INFO - test.py : 28 line - This is INFO of logger2 !! | |
2022-08-02 01:23:05,688 - logger2 - WARNING - test.py : 29 line - This is WARNING of logger2 !! | |
2022-08-02 01:23:05,688 - logger2 - ERROR - test.py : 30 line - This is ERROR of logger2 !! | |
2022-08-02 01:23:05,688 - logger2 - CRITICAL - test.py : 31 line - This is CRITICAL of logger2 !! |
正文完