14 logging日志系统
写代码的时候,print()能帮你调试,但到了生产环境,print()就不够用了——你不知道什么时候发生的错误、错误有多严重、错误发生在哪个文件哪一行。logging模块就是干这个的,它是Python内置的日志系统。
一、快速开始
1.1 基本用法
python
import logging
# 基础配置
logging.basicConfig(level=logging.INFO)
# 记录日志
logging.debug("调试信息") # 不会输出(级别太低)
logging.info("普通信息") # 输出
logging.warning("警告信息") # 输出
logging.error("错误信息") # 输出
logging.critical("严重错误") # 输出输出格式:
INFO:root:普通信息
WARNING:root:警告信息
ERROR:root:错误信息
CRITICAL:root:严重错误1.2 日志级别
从低到高5个标准级别:
| 级别 | 数值 | 用途 |
|---|---|---|
DEBUG | 10 | 调试信息,详细的技术细节 |
INFO | 20 | 普通信息,程序正常运行的记录 |
WARNING | 30 | 警告,不影响运行但需要注意 |
ERROR | 40 | 错误,某些功能无法执行 |
CRITICAL | 50 | 严重错误,程序可能无法继续运行 |
设置某个级别后,只会记录该级别及以上的日志。
二、basicConfig配置
2.1 常用参数
python
import logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
filename="app.log",
filemode="w",
encoding="utf-8"
)
logging.info("程序启动")输出到文件app.log:
2026-06-13 10:30:00 - root - INFO - 程序启动2.2 格式化变量
| 变量 | 含义 |
|---|---|
%(asctime)s | 时间 |
%(name)s | Logger名称 |
%(levelname)s | 日志级别名 |
%(levelno)s | 日志级别数值 |
%(message)s | 日志内容 |
%(filename)s | 文件名 |
%(lineno)d | 行号 |
%(funcName)s | 函数名 |
%(pathname)s | 完整路径 |
%(process)d | 进程ID |
%(thread)d | 线程ID |
2.3 同时输出到控制台和文件
python
import logging
# basicConfig只能配置一次,想同时输出到控制台和文件需要用Handler
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# 控制台Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 文件Handler
file_handler = logging.FileHandler("app.log", encoding="utf-8")
file_handler.setLevel(logging.DEBUG)
# 格式化器
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logger.addHandler(file_handler)
logging.info("这条日志会同时出现在控制台和文件")三、Logger对象
3.1 创建Logger
python
import logging
# 创建具名Logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# 添加Handler
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(name)s - %(message)s"))
logger.addHandler(handler)
logger.info("来自my_module的信息")
# 输出: my_module - 来自my_module的信息3.2 Logger层次
用.分隔的Logger名称会形成父子关系:
python
import logging
# 父Logger
parent_logger = logging.getLogger("myapp")
# 子Logger
child_logger = logging.getLogger("myapp.database")
# 子Logger会把日志传播给父Logger
child_logger.info("数据库连接成功")
# 会同时被child_logger和parent_logger的Handler处理3.3 常用方法
python
import logging
logger = logging.getLogger(__name__)
logger.debug("调试") # 10
logger.info("信息") # 20
logger.warning("警告") # 30
logger.error("错误") # 40
logger.critical("严重") # 50
# 记录异常信息(包含堆栈)
try:
1 / 0
except Exception:
logger.exception("发生异常")
# 会自动记录完整的异常堆栈
# 设置级别
logger.setLevel(logging.DEBUG)
# 检查级别
logger.isEnabledFor(logging.DEBUG) # True四、Handler
4.1 常用Handler
| Handler | 用途 |
|---|---|
StreamHandler | 输出到流(默认stderr) |
FileHandler | 输出到文件 |
RotatingFileHandler | 按大小轮转文件 |
TimedRotatingFileHandler | 按时间轮转文件 |
NullHandler | 丢弃所有日志(用于库) |
4.2 RotatingFileHandler
按文件大小轮转,避免日志文件无限增大。
python
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = RotatingFileHandler(
"app.log",
maxBytes=1024 * 1024, # 1MB
backupCount=5, # 保留5个备份
encoding="utf-8"
)
logger.addHandler(handler)当app.log达到1MB时,会自动轮转为app.log.1、app.log.2等。
4.3 TimedRotatingFileHandler
按时间轮转。
python
import logging
from logging.handlers import TimedRotatingFileHandler
handler = TimedRotatingFileHandler(
"app.log",
when="midnight", # 每天午夜轮转
interval=1,
backupCount=30, # 保留30天
encoding="utf-8"
)when参数:'S'(秒)、'M'(分)、'H'(时)、'D'(天)、'midnight'(午夜)、'W0'-'W6'(星期几)。
五、Formatter
5.1 自定义格式
python
import logging
formatter = logging.Formatter(
fmt="%(asctime)s [%(levelname)s] %(name)s:%(lineno)d - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
handler = logging.StreamHandler()
handler.setFormatter(formatter)5.2 不同Handler不同格式
python
import logging
logger = logging.getLogger(__name__)
# 控制台:简洁格式
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s"))
# 文件:详细格式
file_handler = logging.FileHandler("app.log", encoding="utf-8")
file_handler.setFormatter(logging.Formatter(
"%(asctime)s [%(levelname)s] %(name)s:%(lineno)d - %(message)s"
))
logger.addHandler(console_handler)
logger.addHandler(file_handler)六、Filter
6.1 过滤日志
python
import logging
class NoDebugFilter(logging.Filter):
def filter(self, record):
return record.levelno > logging.DEBUG
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.addFilter(NoDebugFilter())
logger.addHandler(handler)
logger.debug("这条不会输出")
logger.info("这条会输出")七、实战配置
7.1 项目日志配置
python
import logging
import logging.config
LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"standard": {
"format": "%(asctime)s [%(levelname)s] %(name)s:%(lineno)d - %(message)s"
},
"simple": {
"format": "%(levelname)s - %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "simple",
"stream": "ext://sys.stdout"
},
"file": {
"class": "logging.handlers.RotatingFileHandler",
"level": "DEBUG",
"formatter": "standard",
"filename": "app.log",
"maxBytes": 10485760, # 10MB
"backupCount": 5,
"encoding": "utf-8"
}
},
"root": {
"level": "DEBUG",
"handlers": ["console", "file"]
}
}
logging.config.dictConfig(LOGGING_CONFIG)7.2 模块化使用
python
# database.py
import logging
logger = logging.getLogger(__name__)
def connect():
logger.info("连接数据库")
# ...
logger.debug("连接成功")
# main.py
import logging
import database
logging.basicConfig(level=logging.INFO)
database.connect()八、常见问题
8.1 重复日志
python
# 错误:多次调用basicConfig或addHandler会导致重复日志
logging.basicConfig()
logging.basicConfig() # 不会生效,但也不会报错
# 正确:只配置一次
# 或者检查是否已有Handler
logger = logging.getLogger(__name__)
if not logger.handlers:
logger.addHandler(handler)8.2 日志不输出
python
# 原因1:级别设置太高
logger.setLevel(logging.WARNING)
logger.info("不会输出") # INFO < WARNING
# 原因2:没有Handler
logger = logging.getLogger("test")
logger.info("不会输出") # 没有Handler
# 原因3:propagate被设为False
logger.propagate = False九、总结
logging模块的核心:
| 组件 | 作用 |
|---|---|
Logger | 记录日志的对象 |
Handler | 决定日志输出到哪里 |
Formatter | 决定日志的格式 |
Filter | 过滤日志 |
常用模式:
python
import logging
# 快速配置
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(message)s")
# 使用
logging.info("程序启动")
# 或者用具名Logger
logger = logging.getLogger(__name__)
logger.info("模块加载完成")记住basicConfig()配置一次,然后到处用logging.info()就够了。生产环境再加上文件Handler和轮转。