简介
Logging模块是Python标准库自带的日志模块,通过日志记录程序的运行情况是一个开发的好习惯,对于错误排查和系统运维都有很大帮助。内置模块提供了四大组件:
- 记录器(Logger):提供应用程序代码直接使用的接口。
- 处理器(Handler):将日志记录(由记录器创建)发送到适当的目的地。
- 筛选器(Filter):提供了更细粒度的功能,用于确定要输出的日志记录。
- 格式器(Formatter):程序在最终输出日志记录的内容格式。
记录器Logger
创建记录器
logger = logging.getLogger()
记录器名称
需要定义多个日志器时,需要指定日志器名称,不然结果就演化成多份相同的日志器了(Handler叠加现象)
-
若没有指定记录器名称,默认为root
import logging logger = logging.getLogger() # 记录器名称默认为root logger1 = logging.getLogger() # 记录器名称默认为root logger2 = logging.getLogger() # 记录器名称默认为root # 设置三个处理器handler console_handler = logging.StreamHandler() console_handler1 = logging.StreamHandler() console_handler2 = logging.StreamHandler() # 给三个相同名称的logger添加上处理器;可以理解成给一个日志器加三个处理器,这一个日志器可以用logger、logger1、logger2任意一个表示 logger.addHandler(console_handler) logger1.addHandler(console_handler1) logger2.addHandler(console_handler2) # 设置一下格式 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') console_handler.setFormatter(formatter) console_handler1.setFormatter(formatter) console_handler2.setFormatter(formatter) # 输出日志记录 logger1.warning("输出一条日志记录") """ 2023-06-22 09:28:50,102 - root - WARNING - 输出一条日志记录 2023-06-22 09:28:50,102 - root - WARNING - 输出一条日志记录 2023-06-22 09:28:50,102 - root - WARNING - 输出一条日志记录 """
-
通常开发过程中,需要指定记录器名器,来规避handler叠加现象
logger = logging.getLogger(__name__) # 日志器名称为包名 logger1 = logging.getLogger("DIY") # 日志器名称为DIY print(logger) # <Logger __main__ (WARNING)> print(logger1) # <Logger DIY (WARNING)>
处理器Handler
处理器负责将日志消息发送到不同目标,例如:控制台,日志文件,电子邮件,等目标
处理器类型 | 描述 |
---|---|
StreamHandler |
将日志消息发送到流(默认是sys.stderr ),类似文件的对象 |
FileHandler |
将日志消息发送到磁盘文件 |
NullHandler |
禁止任何日志消息的处理 |
WatchedFileHandler |
将日志消息发送到文件,并监视文件的变化 |
SocketHandler |
将日志消息发送到网络套接字 |
DatagramHandler |
将日志消息作为UDP数据包发送到指定的主机和端口 |
SMTPHandler |
将日志消息通过电子邮件发送 |
SysLogHandler |
将日志消息发送到系统日志 |
NTEventLogHandler |
将日志消息发送到Windows NT/2000/XP的事件日志 |
MemoryHandler |
将日志消息缓存到内存中,并在达到指定容量时发送到其他处理器 |
RotatingFileHandler |
将日志消息发送到文件,并在文件大小达到一定限制时进行轮转 |
TimedRotatingFileHandler |
将日志消息发送到文件,并在时间间隔或文件大小达到一定限制时进行轮转 |
HTTPHandler |
将日志消息发送到HTTP服务器 |
QueueHandler |
将日志消息发送到队列中,供其他线程处理 |
QueueListener |
从队列中获取日志消息,并将其发送到其他处理器 |
StreamHandler
日志记录发送到控制台中
-
用法
logging.StreamHandler(stream=None)
-
参数说明
扫描二维码关注公众号,回复: 16900591 查看本文章- stream:默认为none,日志输出到sys.stderr;指定stream的话,则日志输出到指定的steam
FileHandler
日志记录发送到磁盘文件中
-
用法
logging.FileHandler(filename, mode='a', encoding=None, delay=False)
-
参数说明
- filename:只填写文件名称,则默认新增文件到当前工作目录;填写工作路径+文件名称,则新增到对应工作目录
- mode:默认a模式,可自定义
- encoding:默认为None,可自定义
- delay:默认为False,为True时,将文件打开延迟到第一次调用时进行emit()
NullHandler
不做任何事情的处理器,防止sys.stderr在没有日志记录配置的情况下将库的已记录事件输出
-
用法
logging.NullHandler()
WatchedFileHandler
该处理器旨在在Unix / Linux下使用,它监视文件以查看自上一次发出以来是否已更改。(如果文件的设备或索引节点已更改,则认为该文件已更改。)如果文件已更改,则关闭旧文件流,并打开该文件以获取新的流,不适合在Windows下使用
-
用法
logging.handlers.WatchedFileHandler(filename, mode='a', encoding=None, delay=False)
根据日志文件大小轮转 RotatingFileHandler
日志记录到文件中,且支持指定日志文件大小,备份文件数量
-
用法
logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)
-
参数说明
-
filenam:要写入的日志文件名。
-
mode:文件打开模式,默认为追加模式a。
-
maxBytes:日志文件大小,单位为字节。当达到该大小限制时,将触发文件轮转。默认值为0,表示没有大小限制。
-
backupCount:备份文件数量。默认值为0,表示不保留备份文件。
假设backupCount设置为3,并且已经存在app.log、app.log.1、app.log.2、app.log.3四个文件。当app.log文件大小达到或超过maxBytes限制时,文件轮转会按照以下步骤进行: app.log.3文件将被删除。 app.log.2将重命名为app.log.3。 app.log.1将重命名为app.log.2。 app.log将重命名为app.log.1。 创建一个新的空app.log文件用于继续记录日志消息。 现在,你将有app.log、app.log.1、app.log.2和app.log.3四个文件。旧备份文件的数字会递增,最新的日志总是记录在app.log文件中。 当app.log再次达到或超过maxBytes限制时,将重复这个轮转过程,删除最旧的备份文件(此时是app.log.3),重命名其他备份文件,并创建一个新的空app.log文件。
-
encoding:文件编码方式。默认为None,表示使用系统默认编码。
-
delay:是否在打开文件之前延迟。默认为False,表示立即打开文件。
-
根据文件创建时间轮转 TimedRotatingFileHandler
日志记录到文件中,支持按时间间隔来更新日志
-
用法
logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None)
-
参数说明
filename
:要写入的日志文件名。when
:指定时间间隔的单位。默认为'h'
,表示按小时进行轮转。其他可选值有'S'
(秒)、'M'
(分钟)、'D'
(天)、'W0'
至'W6'
(每周的某天)、'midnight'
(每天凌晨)。interval
:时间间隔的数值。默认为1,表示每个时间间隔进行轮转。backupCount
:要保留的备份文件的数量。默认值为0,表示不保留备份文件。encoding
:文件编码方式。默认为None
,表示使用系统默认编码。delay
:是否在打开文件之前延迟。默认为False
,表示立即打开文件。utc
:是否使用UTC时间。默认为False
,表示使用本地时间。atTime
:指定在一天中的特定时间执行轮转。当when
设置为'midnight'
时,可以指定具体的时间,格式为'%H:%M:%S'
。
过滤器Filter
用于对日志消息进行筛选和过滤,决定哪些日志消息应该被记录下来,哪些应该被忽略。过滤器可以被应用于日志记录器(
Logger
)或处理器(Handler
)。
-
官网给出的示例
import logging from random import choice class ContextFilter(logging.Filter): """ This is a filter which injects contextual information into the log. Rather than use actual contextual information, we just use random data in this demo. """ USERS = ['jim', 'fred', 'sheila'] IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1'] def filter(self, record): record.ip = choice(ContextFilter.IPS) record.user = choice(ContextFilter.USERS) return True if __name__ == '__main__': levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) logging.basicConfig(level=logging.DEBUG, format='%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s') a1 = logging.getLogger('a.b.c') a2 = logging.getLogger('d.e.f') f = ContextFilter() a1.addFilter(f) a2.addFilter(f) a1.debug('A debug message') a1.info('An info message with %s', 'some parameters') for x in range(10): lvl = choice(levels) lvlname = logging.getLevelName(lvl) a2.log(lvl, 'A message at %s level with %d %s', lvlname, 2, 'parameters') """ 2023-06-22 10:58:48,965 a.b.c DEBUG IP: 127.0.0.1 User: jim A debug message 2023-06-22 10:58:48,965 a.b.c INFO IP: 192.168.0.1 User: sheila An info message with some parameters 2023-06-22 10:58:48,965 d.e.f WARNING IP: 192.168.0.1 User: sheila A message at WARNING level with 2 parameters 2023-06-22 10:58:48,965 d.e.f DEBUG IP: 123.231.231.123 User: jim A message at DEBUG level with 2 parameters 2023-06-22 10:58:48,965 d.e.f DEBUG IP: 127.0.0.1 User: sheila A message at DEBUG level with 2 parameters 2023-06-22 10:58:48,965 d.e.f ERROR IP: 123.231.231.123 User: sheila A message at ERROR level with 2 parameters 2023-06-22 10:58:48,965 d.e.f INFO IP: 123.231.231.123 User: fred A message at INFO level with 2 parameters 2023-06-22 10:58:48,965 d.e.f INFO IP: 127.0.0.1 User: sheila A message at INFO level with 2 parameters 2023-06-22 10:58:48,965 d.e.f WARNING IP: 127.0.0.1 User: sheila A message at WARNING level with 2 parameters 2023-06-22 10:58:48,965 d.e.f DEBUG IP: 127.0.0.1 User: sheila A message at DEBUG level with 2 parameters 2023-06-22 10:58:48,965 d.e.f CRITICAL IP: 192.168.0.1 User: fred A message at CRITICAL level with 2 parameters 2023-06-22 10:58:48,965 d.e.f WARNING IP: 123.231.231.123 User: fred A message at WARNING level with 2 parameters """
格式器Formatter
格式器可以初始化日志记录的内容格式,结合LogRecord对象提供的属性,可以设置不同的日志格式
每个处理器只能有一个formatter,当给处理器多次设置formatter时,以最后一个为准
-
语法
logging.Formatter(fmt=None, datefmt=None, style='%')
-
参数说明
- fmt:日志格式参数,默认为None,如果不特别指定fmt,则使用’%(message)s’格式
- datafmt:时间格式参数,默认为None,如果不特别指定datafmt,则使用formatTime()文档中描述的格式。
- style:风格参数,默认为’%’,也支持’$’,’{'格式
LogRecord的属性
属性 | 描述 | 类型 | 格式化符号 |
---|---|---|---|
args | 用于格式化消息的参数 | 元组 | %(args)s |
asctime | 格式化的可读时间字符串 | 字符串 | %(asctime)s |
created | 记录的创建时间(自epoch开始的秒数) | 浮点数 | %(created)f |
exc_info | 异常信息(sys.exc_info的返回值) | 元组 | %(exc_info)s |
exc_text | 异常信息的字符串表示 | 字符串 | %(exc_text)s |
filename | 创建日志记录的源文件的文件名 | 字符串 | %(filename)s |
funcName | 创建日志记录的函数名 | 字符串 | %(funcName)s |
levelname | 日志级别名称 | 字符串 | %(levelname)s |
levelno | 日志级别的数值 | 整数 | %(levelno)s |
lineno | 创建日志记录的源文件的行号 | 整数 | %(lineno)d |
module | 创建日志记录的模块名 | 字符串 | %(module)s |
msg | 日志消息 | 字符串 | %(message)s |
msecs | 毫秒部分的时间戳 | 整数 | %(msecs)d |
name | 记录器名称 | 字符串 | %(name)s |
pathname | 创建日志记录的源文件的完整路径 | 字符串 | %(pathname)s |
process | 进程ID | 整数 | %(process)d |
processName | 进程名称 | 字符串 | %(processName)s |
relativeCreated | 记录相对于logging模块加载的时间(以毫秒为单位) | 浮点数 | %(relativeCreated)f |
stack_info | 栈信息字符串 | 字符串 | %(stack_info)s |
thread | 线程ID | 整数 | %(thread)d |
threadName | 线程名称 | 字符串 | %(threadName)s |
日志等级
指定了日志等级后,只会显示大于等于所指定日志等级的日志信息!
logging中级别大小:DEBUG<INFO<WARNING<ERROR<CRITICAL
日志等级(level) | 描述 | 数值 |
---|---|---|
0 | ||
DEBUG | 调试信息,通常在诊断问题的时候用 | 10 |
INFO | 普通信息,确认程序按照预期运行 | 20 |
WARNING(默认级别) | 警告信息,表示发生意想不到的事,或者指示接下来可能会出现一些问题,但是程序还是继续运行 | 30 |
ERROR | 错误信息,程序运行中出现了一些问题,程序某些功能不能执行 | 40 |
CRITICAL | 危险信息,一个严重的错误,导致程序无法继续运行 | 50 |
给记录器设置日志级别
记录器设置的日志级别会作用于该记录器下所有的处理器
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
给处理器设置日志级别
如果同时设置了记录器、处理器的日志级别,那么以最高的那个级别作为标准输出
import logging
logger = logging.getLogger(__name__)
# 设置两个处理器
console_handler = logging.StreamHandler()
console_handler1 = logging.StreamHandler()
# 设置处理器日志级别分别INFO,WARNING
console_handler.setLevel(logging.INFO)
console_handler1.setLevel(logging.WARNING)
输出日志记录
logging模块提供5个日志级别的输出方法(debug、info、warning、erro、critical)
调用方式
-
直接通过logging调用
import logging logging.error("aaaaa")
-
通过创建记录器对象
import logging logger = logging.getLogger('logger1') # 获取配置中的logger对象 # 输出logger日志记录 logger.error("====================【开始测试】====================")
输出日志参数说明
-
语法
import logging logger = logging.getLogger('logger1') logger.debug(msg, *args, **kwargs)
-
参数
- msg:日志消息格式字符串
- args:args是被合并到参数 MSG使用字符串格式化操作,当未提供args时,不会对msg执行%格式化操作。
- kwargs:kwargs中检查了三个关键字参数: exc_info,stack_info和extra。
- exc_info:是否将异常信息添加到日志消息的参数,默认为False,若不为False,则将异常信息添加到日志消息中。
- stack_info:是否将堆栈信息添加到日志消息的参数,默认为False,若不为False,则将堆栈信息添加到日志消息中
- extra:可用于传递字典,该字典用于使用LogRecord 用户定义的属性填充为日志记录事件创建的__dict__ 。然后可以根据需要使用这些自定义属性。
exc_info实例(将异常信息添加到日志中)
import logging
logger = logging.getLogger('logger1') # 获取配置中的logger对象
logger.setLevel(logging.INFO)
console_handler = logging.StreamHandler()
# console_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
try:
open("notreal.txt", "rb")
except Exception:
logger.info("Faild to open notreal.txt from logger.debug", exc_info=True)
"""
2023-06-22 11:42:14,166 - logger1 - INFO - Faild to open notreal.txt from logger.debug
Traceback (most recent call last):
File "/Users/meta/GitHub/flaskProject/conf/log_conf.py", line 14, in <module>
open("notreal.txt", "rb")
FileNotFoundError: [Errno 2] No such file or directory: 'notreal.txt'
"""
stack_info实例(堆栈信息:异常的位置)
logger.info("====================【开始测试】====================", stack_info=True)
"""
2023-06-22 11:46:23,240 - logger1 - INFO - ====================【开始测试】====================
Stack (most recent call last):
File "/Users/meta/GitHub/flaskProject/conf/log_conf.py", line 19, in <module>
logger.info("====================【开始测试】====================", stack_info=True)
"""
extra实例
FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logger = logging.getLogger('tcpserver')
logger.warning('Protocol problem: %s', 'connection reset', extra=d)
"""
2023-06-22 11:48:19,350 192.168.0.1 fbloggs Protocol problem: connection reset
"""
日志记录配置
logging模块提供一个配置方法,以字典为容器,设置日志器、处理器、格式器等参数,使用
logging.config.dictConfig(配置字典)
方法生成对应的元器件
-
方式一:
import logging.handlers import logging.config config = { 'version': 1, # 必填项,值只能为1 'disable_existing_loggers': True, # 选填,默认为True,将以向后兼容的方式启用旧行为,此行为是禁用任何现有的非根日志记录器,除非它们或它们的祖先在日志配置中显式命名。如果指定为False,则在进行此调用时存在的记录器将保持启用状态 'incremental': False, # 选填,默认为False,作用,为True时,logging完全忽略任何formatters和filters,仅处理handlers的level 'formatters': # 格式器配置专用key,在这里配置formatter,可配置复数formatter { 'myformatter1': { 'class': 'logging.Formatter', # 必填,格式器对应的类 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s', # fmt格式 'datefmt': '%Y-%m-%d %H:%M:%S' # 日期时间格式 }, # 'myformatter2': { # '()': 'my_diy_formatter', # 将class改为(),代表不使用logging的类,使用我们重新定义的类 # 'format': '%(asctime)s - %(levelname)s - %(message)s', # fmt格式 # 'datefmt': '%Y-%m-%d %H:%M:%S' # 日期时间格式 # } }, 'handlers': # 处理器配置专用key,在这里配置handler,可配置复数handler { 'console_handler': { 'class': 'logging.StreamHandler', # 必填,处理器对应的类 'level': logging.DEBUG, # 选填,处理器的日志级别,可填字符串'info',或者logging.INFO 'formatter': 'myformatter1', # 选填,这里要填写formatters字典中的键 }, 'file_handler': { 'class': 'logging.handlers.RotatingFileHandler', # 必填,处理器对应的类 'level': logging.INFO, # 选填,处理器的日志级别,可填字符串'info',或者logging.INFO 'formatter': 'myformatter1', # 选填,这里要填写formatters字典中的键 'filename': './mylog.log', # filehandler特有参数,文件名 'maxBytes': 1024*1024, # 文件大小 'backupCount': 3, # 备份数量 'encoding': 'UTF-8', # 编码格式 } }, 'loggers': # 记录器配置专用key,在这里配置logger,可配置复数logger { 'logger1': { 'handlers': ['console_handler', 'file_handler'], # 列表形式,元素填handlers字典中的handler 'level': logging.DEBUG, # 选填,记录器的日志级别,不填则默认Warning级别 'propagate': False, # 选填,为False时,禁止将日志消息传递给父级记录器 } }, 'root': # 根记录器专用key { 'handlers': ['console_handler', 'file_handler'], # 列表形式,元素填handlers字典中的handler 'level': logging.DEBUG, # 选填,记录器的日志级别,不填则默认Warning级别 } } # 根据配置字典,配置对应元器件 logging.config.dictConfig(config) if name == 'main': logger = logging.getLogger('logger1') # 获取配置中的logger对象 # 输出logger日志记录 logger.debug("====================【开始测试】====================") logger.info("====================【开始测试】====================") logger.warning("====================【开始测试】====================") logger.error("====================【开始测试】====================") logger.critical("====================【开始测试】====================")
-
方式二
# json文件 { "version": 1, "disable_existing_loggers": true, "incremental": false, "formatters": { "formatter1": { "class": "logging.Formatter", "format": "%(asctime)s - %(name)s - %(process)d - %(thread)d - %(message)s", "datefmt": "%Y-%m-%d %H:%M:%S" } }, "handlers": { "console_handler": { "class": "logging.StreamHandler", "level": "DEBUG", "formatter": "formatter1" }, "info_handler": { "class": "logging.handlers.RotatingFileHandler", "level": "INFO", "formatter": "formatter1", "filename": "{ {ROOT_PATH}}/logs/info.log", "maxBytes": 1048576, "backupCount": 3, "encoding": "UTF-8" }, "warning_handler": { "class": "logging.handlers.RotatingFileHandler", "level": "WARNING", "formatter": "formatter1", "filename": "{ {ROOT_PATH}}/logs/warning.log", "maxBytes": 1048576, "backupCount": 3, "encoding": "UTF-8" }, "error_handler": { "class": "logging.handlers.RotatingFileHandler", "level": "ERROR", "formatter": "formatter1", "filename": "{ {ROOT_PATH}}/logs/error.log", "maxBytes": 1048576, "backupCount": 3, "encoding": "UTF-8" } }, "loggers": { "log.debug": { "handlers": [ "console_handler" ], "level": "DEBUG", "propagate": false }, "log.info": { "handlers": [ "info_handler" ], "level": "INFO", "propagate": false }, "log.warning": { "handlers": [ "warning_handler" ], "level": "WARNING", "propagate": false }, "log.error": { "handlers": [ "error_handler" ], "level": "ERROR", "propagate": false } }, "root": { "handlers": [ "console_handler" ], "level": "DEBUG" } } # 自定义封装一个类 import json import logging.config from apps.src.utils.const import ROOT_PATH with open(f"{ROOT_PATH}/conf/log.json", "r") as f: log_json_str = f.read().replace("{ {ROOT_PATH}}", ROOT_PATH) log_json = json.loads(log_json_str) if log_json_str else {} # 根据配置字典,配置对应元器件 logging.config.dictConfig(log_json) _info_log = logging.getLogger("log.info") _warning_log = logging.getLogger("log.warning") _error_log = logging.getLogger("log.error") _debug_log = logging.getLogger("log.debug") class ilog(object): info = _info_log.info debug = _debug_log.debug warning = _warning_log.warning error = _error_log.error