赞
踩
Logging模块是Python标准库自带的日志模块,通过日志记录程序的运行情况是一个开发的好习惯,对于错误排查和系统运维都有很大帮助。内置模块提供了四大组件:
- 记录器(Logger):提供应用程序代码直接使用的接口。
- 处理器(Handler):将日志记录(由记录器创建)发送到适当的目的地。
- 筛选器(Filter):提供了更细粒度的功能,用于确定要输出的日志记录。
- 格式器(Formatter):程序在最终输出日志记录的内容格式。
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)>
处理器负责将日志消息发送到不同目标,例如:控制台,日志文件,电子邮件,等目标
处理器类型 | 描述 |
---|---|
StreamHandler | 将日志消息发送到流(默认是sys.stderr ),类似文件的对象 |
FileHandler | 将日志消息发送到磁盘文件 |
NullHandler | 禁止任何日志消息的处理 |
WatchedFileHandler | 将日志消息发送到文件,并监视文件的变化 |
SocketHandler | 将日志消息发送到网络套接字 |
DatagramHandler | 将日志消息作为UDP数据包发送到指定的主机和端口 |
SMTPHandler | 将日志消息通过电子邮件发送 |
SysLogHandler | 将日志消息发送到系统日志 |
NTEventLogHandler | 将日志消息发送到Windows NT/2000/XP的事件日志 |
MemoryHandler | 将日志消息缓存到内存中,并在达到指定容量时发送到其他处理器 |
RotatingFileHandler | 将日志消息发送到文件,并在文件大小达到一定限制时进行轮转 |
TimedRotatingFileHandler | 将日志消息发送到文件,并在时间间隔或文件大小达到一定限制时进行轮转 |
HTTPHandler | 将日志消息发送到HTTP服务器 |
QueueHandler | 将日志消息发送到队列中,供其他线程处理 |
QueueListener | 从队列中获取日志消息,并将其发送到其他处理器 |
日志记录发送到控制台中
用法
logging.StreamHandler(stream=None)
参数说明
日志记录发送到磁盘文件中
用法
logging.FileHandler(filename, mode='a', encoding=None, delay=False)
参数说明
不做任何事情的处理器,防止sys.stderr在没有日志记录配置的情况下将库的已记录事件输出
用法
logging.NullHandler()
该处理器旨在在Unix / Linux下使用,它监视文件以查看自上一次发出以来是否已更改。(如果文件的设备或索引节点已更改,则认为该文件已更改。)如果文件已更改,则关闭旧文件流,并打开该文件以获取新的流,不适合在Windows下使用
用法
logging.handlers.WatchedFileHandler(filename, mode='a', encoding=None, delay=False)
日志记录到文件中,且支持指定日志文件大小,备份文件数量
用法
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,表示立即打开文件。
日志记录到文件中,支持按时间间隔来更新日志
用法
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'
。用于对日志消息进行筛选和过滤,决定哪些日志消息应该被记录下来,哪些应该被忽略。过滤器可以被应用于日志记录器(
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 """
格式器可以初始化日志记录的内容格式,结合LogRecord对象提供的属性,可以设置不同的日志格式
每个处理器只能有一个formatter,当给处理器多次设置formatter时,以最后一个为准
语法
logging.Formatter(fmt=None, datefmt=None, style='%')
参数说明
属性 | 描述 | 类型 | 格式化符号 |
---|---|---|---|
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)
参数
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' """
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)
"""
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。