【Python】Logging模块调试详解与实用代码示例
文章目录
1. logging基本使用
logging日志级别:
级别 | 数值 | 使用场景示例 | 生产环境建议 |
---|---|---|---|
DEBUG | 10 | 变量跟踪、算法中间状态 | 关闭 |
INFO | 20 | 服务启动、关键业务流程节点 | 保留 |
WARNING | 30 | 低磁盘空间、非预期参数但可处理 | 保留 |
ERROR | 40 | 数据库连接失败、文件解析异常 | 保留 |
CRITICAL | 50 | 系统崩溃、核心服务不可用 | 保留 |
默认日志级别warning
使用logging向控制台输出日志代码示例:
import logging
# 默认日志输出级别warning
# 指定输出级别basicConfig
logging.basicConfig(level=logging.DEBUG)
# 能保证logging输出顺序一致,但是能保证logging与print输出顺序一致
# 可能会出现先输出logging,再输出print的情况, 内部日志输出机制不是同步的, 异步写入, 支持高并发
print("this is print log")
logging.debug("This is Debug log")
logging.info("This is info log")
logging.warning("This is warning log")
logging.error("This is error log")
logging.critical("This is citical log")
logging将日志输出到文件:
import logging
# 默认日志输出级别warning
# 指定输出日志文件、日志记录级别 basicConfig
# 如果没有会新建demo.log文件,若有则会在文件后追加log日志
# filemode = "w"先清空再写入, "a"是追加写入, 默认是追加写入
logging.basicConfig(filename='demo.log', filemode="w", encoding="utf-8", level=logging.DEBUG)
logging.debug("This is Debug log")
logging.info("This is info log")
logging.warning("This is warning log")
logging.error("This is error log")
logging.critical("This is citical log")
logging输出变量:
import logging
# 向日志输出变量
logging.basicConfig(level=logging.DEBUG)
name = "张三"
age = 18
logging.debug("This is Debug log")
logging.debug("姓名 %s, 年龄 %d", name, age)
logging.debug("姓名 %s, 年龄 %d" % (name, age))
logging.debug("姓名 {}, 年龄{}".format(name, age))
logging.debug(f"姓名 {name}, 年龄{age}")
# 输出
# DEBUG:root:This is Debug log
# DEBUG:root:姓名 张三, 年龄 18
# DEBUG:root:姓名 张三, 年龄 18
# DEBUG:root:姓名 张三, 年龄18
# DEBUG:root:姓名 张三, 年龄18
输出日期:
import logging
# 输出格式和添加一些公共变量, 时间,日志级别,所在行数,信息
# datafmt 设置时间格式
logging.basicConfig(format="%(asctime)s | %(levelname)s | %(filename)s:%(lineno)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
level=logging.DEBUG)
name = "张三"
age = 18
logging.debug("姓名 %s, 年龄 %d", name, age)
# 输出:
# 2025-03-13 08:10:33 | DEBUG | mylog.py:11 | 姓名 张三, 年龄 18
2. logging的高级应用
logging模块采用了模块化设计,主要包含四种组件:
一个记录器可以对应多个处理器
loggers记录器:
-
提供应用程序的调用接口
logger = logging.getLogger(__name__) -
决定日志记录的级别
logger.setLevel() -
将日志内容传递到相关联的handlers中:
logger.addHandler() 和 logger.removeHandler()
只要 __name__
不变,logger就是单例的
Handlers处理器:
他们将日志分发到不同的目的地。可以是文件、标准输出、邮件、或者socket、http等协议发送到任何地方
StreamHandler:
FileHandler:
其它Handdlers处理器:
FileHandler: 只会往一个文件中写,需要人为备份
RotatingFileHandler: 按照文件大小分别写入日志
TimedRotatingFileHandler:按照时间写入日志
Formatters格式:
Formatters对象用来最终设置日志信息的顺序、结构和内容
其构造方法为:ft = logging.Formatter.__init__(fmt=None, datefmt=None, style=’ %')
datefmt 默认是 %Y-%m-%d %H:%M:%S样式的
style 参数默认为百分符 %, 这表示 %(<dictionary key>)s 格式的字符串
Python代码示例:
import logging
# 记录器
logger = logging.getLogger('applog')
logger.setLevel(logging.DEBUG)
# 处理器handler
# 向终端输出
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.INFO)
# 向文件输出,没有设置日志级别,将使用logger的级别
fileHnadler = logging.FileHandler(filename="addDemo.log")
fileHnadler.setLevel(logging.INFO)
# Formatter格式
formatter = logging.Formatter("%(asctime)s|%(levelname)8s|%(filename)s:%(lineno)4s|%(message)s")
# 给处理器设置格式
consoleHandler.setFormatter(formatter)
fileHnadler.setFormatter(formatter)
# 记录器设置处理器,会向控制台和 文件同时写入日志
logger.addHandler(consoleHandler)
logger.addHandler(fileHnadler)
# 定义一个过滤器
flt = logging.Filter("cn.cccb") # logger现在的名字是 app.log, 不满足以cn.cccb开头,所以没有日志输出
# 关联过滤器
# logger.addFilter(flt) # 记录器关联过滤器
fileHnadler.addFilter(flt) # 处理器关联过滤器
# 打印日志代码
logger.debug("This is debug log")
logger.info("This is info log")
logger.warning("This is warning log")
logger.error("This is error log")
logger.critical("This is critical log")
使用配置文件的形式:
测试代码:
import logging
import logging.config
# 配置文件方式来处理日志
# 记录器
logging.config.fileConfig('logging.conf')
rootLogger = logging.getLogger()
rootLogger.debug("this is root Logger, debug")
logger = logging.getLogger('applog')
logger.debug("This is applog Logger , debug")
a = 'abc'
try:
int(a)
except Exception as e:
logger.exception(e) # 将报错输出到日志
# 1. 代码形式
# 2. 配置文件形式
# 3. 字典形式
logging.conf :
[loggers]
keys = root,applog
[handlers]
keys = fileHandler, consoleHandler
[formatters]
keys = simpleFormatter
[logger_root]
level = DEBUG
handlers = consoleHandler
[logger_applog]
level = DEBUG
handlers = fileHandler,consoleHandler
qualname = applog
propagate = 0
[handler_consoleHandler]
class = StreamHandler
args = (sys.stdout,)
level = DEBUG
formatter = simpleFormatter
[handler_fileHandler]
class = logging.handlers.TimedRotatingFileHandler
# 参数说明:filename, when, interval, backupCount
args = ('applog.log', 'midnight',1,0)
level = DEBUG
formatter = simpleFormatter
[formatter_simpleFormatter]
format = %(asctime)s|%(levelname)8s|%(filename)s:%(lineno)4d|%(message)s
datefmt = %Y-%m-%d %H:%M:%S
本文参考: https://www.bilibili.com/video/BV1sK4y1x7e1/?spm_id_from=333.1387.favlist.content.click&vd_source=cf0b4c9c919d381324e8f3466e714d7a
3. logging初始化封装示例
import os
import logging
# log 日志相关设置
class LoggerConfig(Enum):
LOG_NAME = "WPF"
LOG_DIR = "logs"
LOGGER_FORMATTER = "%(asctime)s|%(levelname)8s|%(filename)s:%(lineno)4s|%(message)s"
LOGGER_LEVEL = logging.DEBUG # logger日志级别
LOGGER_MODE = 0 # 0:控制台和文件日志输出 1:控制台日志输出 2: 文件日志输出
def setup_logging(
name = LoggerConfig.LOG_NAME.value,
log_dir = LoggerConfig.LOG_DIR.value,
logger_formatter = LoggerConfig.LOGGER_FORMATTER.value,
level = LoggerConfig.LOGGER_LEVEL.value,
mode = LoggerConfig.LOGGER_MODE.value
):
"""
初始化日志
Args:
name: log 名称
log_dir: 日志存放目录
logger_formatter: 日志格式
level: 日志级别
mode: 0、1、2 0:控制台和文件日志输出 1:控制台日志输出 2: 文件日志输出
"""
# 如果日志目录不存在则创建
try:
os.makedirs(log_dir, exist_ok=True)
except Exception as e:
raise RuntimeError(f"创建日志目录失败: {log_dir}") from e
# 记录器
logger = logging.getLogger(name)
logger.setLevel(level)
# 格式
formatter = logging.Formatter(logger_formatter)
# 终端输出处理器
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(level)
consoleHandler.setFormatter(formatter)
# 文件输出处理器
log_file = os.path.join(log_dir, f"{name}.log")
fileHandle = logging.FileHandler(log_file)
fileHandle.setLevel(level)
fileHandle.setFormatter(formatter)
# 模式
if mode == 0:
logger.addHandler(consoleHandler)
logger.addHandler(fileHandle)
elif mode ==1:
logger.addHandler(consoleHandler)
elif mode==2:
logger.addHandler(fileHandle)
else:
raise ValueError(f"无效的模式参数: {mode},可选值 0/1/2")
return logger
logger = setup_logging()
if __name__ == "__main__":
pass
作者:小小小小祥