Python 日志处理 logging
目录
学习目标
- 掌握 logging 模块的基本用法
- 理解日志级别和日志记录器的层次结构
- 学会配置日志处理器和格式化器
- 掌握日志配置文件的使用
1. 基础用法
1.1 简单日志输出
import logging
# 基本配置(一次性)
logging.basicConfig(level=logging.INFO)
# 不同级别的日志
logging.debug("调试信息") # 最详细
logging.info("普通信息") # 一般信息
logging.warning("警告信息") # 警告
logging.error("错误信息") # 错误
logging.critical("严重错误") # 最严重
# 输出:
# WARNING:root:警告信息
# ERROR:root:错误信息
# CRITICAL:root:严重错误
# (默认级别是 WARNING,低于此级别的不会显示)1.2 基本配置
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info("程序启动")
logging.debug("调试: x = %d", 42)
# 输出:
# 2024-01-15 14:30:00 - INFO - 程序启动
# 2024-01-15 14:30:00 - DEBUG - 调试: x = 422. 日志级别
| 级别 | 数值 | 用途 |
|---|---|---|
| DEBUG | 10 | 详细的调试信息 |
| INFO | 20 | 确认程序按预期运行 |
| WARNING | 30 | 警告,但程序仍继续运行 |
| ERROR | 40 | 错误,某项功能无法正常执行 |
| CRITICAL | 50 | 严重错误,程序可能无法继续 |
import logging
# 设置级别
logger = logging.getLogger('myapp')
logger.setLevel(logging.DEBUG)
# 检查级别
print(logger.isEnabledFor(logging.DEBUG)) # True
print(logger.isEnabledFor(logging.INFO)) # True
print(logger.isEnabledFor(logging.WARNING)) # True3. 日志记录器层次结构
import logging
# 创建命名记录器
# 层次: root -> app -> app.module -> app.module.sub
logger = logging.getLogger('app.module')
# 子记录器继承父记录器的配置
parent = logging.getLogger('app')
child = logging.getLogger('app.module')
grandchild = logging.getLogger('app.module.sub')
# 设置父记录器级别
parent.setLevel(logging.WARNING)
# 子记录器继承级别
print(child.level) # 0(继承父级别)
print(child.getEffectiveLevel()) # 30(WARNING)
# 子记录器可独立设置
child.setLevel(logging.DEBUG)
print(child.getEffectiveLevel()) # 10(DEBUG)4. 处理器和格式化器
4.1 输出到控制台
import logging
# 创建记录器
logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)
# 创建控制台处理器
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)
# 使用
logger.info("普通信息")
logger.debug("调试信息(不会显示,因为处理器级别是 INFO)")4.2 输出到文件
import logging
logger = logging.getLogger('filelogger')
logger.setLevel(logging.DEBUG)
# 文件处理器
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
# 格式化器
formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] %(filename)s:%(lineno)d - %(message)s'
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.info("写入日志文件")
logger.error("发生错误")4.3 多处理器组合
import logging
logger = logging.getLogger('multi')
logger.setLevel(logging.DEBUG)
# 控制台处理器 - 只显示 WARNING 及以上
console = logging.StreamHandler()
console.setLevel(logging.WARNING)
console.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
# 文件处理器 - 记录所有级别
file_handler = logging.FileHandler('debug.log', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s [%(levelname)s] %(message)s'
))
# 添加两个处理器
logger.addHandler(console)
logger.addHandler(file_handler)
# 使用
logger.debug("调试信息(只在文件)")
logger.info("普通信息(只在文件)")
logger.warning("警告(文件+控制台)")
logger.error("错误(文件+控制台)")5. 日志轮转
5.1 按大小轮转
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger('rotating')
logger.setLevel(logging.DEBUG)
# 文件最大 1MB,保留 3 个备份
handler = RotatingFileHandler(
'app.log',
maxBytes=1024 * 1024, # 1MB
backupCount=3,
encoding='utf-8'
)
handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
logger.addHandler(handler)
# 大量日志测试
for i in range(10000):
logger.info(f"日志行 {i}: " + "x" * 100)5.2 按时间轮转
import logging
from logging.handlers import TimedRotatingFileHandler
logger = logging.getLogger('timed')
logger.setLevel(logging.DEBUG)
# 每天轮转,保留 7 天
handler = TimedRotatingFileHandler(
'app.log',
when='midnight', # 每天午夜轮转
interval=1,
backupCount=7,
encoding='utf-8'
)
handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
logger.addHandler(handler)6. 配置方式
6.1 代码配置
import logging
def setup_logging():
"""通过代码配置日志"""
# 根记录器
root = logging.getLogger()
root.setLevel(logging.DEBUG)
# 控制台处理器
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
root.addHandler(console)
# 文件处理器
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s [%(levelname)s] %(name)s: %(message)s'
))
root.addHandler(file_handler)
setup_logging()
logging.info("日志已配置")6.2 字典配置(推荐)
import logging
import logging.config
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
'simple': {
'format': '%(levelname)s: %(message)s'
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'simple',
},
'file': {
'class': 'logging.FileHandler',
'filename': 'app.log',
'level': 'DEBUG',
'formatter': 'standard',
'encoding': 'utf-8',
},
},
'loggers': {
'myapp': {
'level': 'DEBUG',
'handlers': ['console', 'file'],
'propagate': False,
},
},
'root': {
'level': 'WARNING',
'handlers': ['console'],
},
}
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger('myapp')
logger.info("使用字典配置")6.3 配置文件
# logging.conf
[loggers]
keys=root,myapp
[handlers]
keys=console,file
[formatters]
keys=standard,simple
[logger_root]
level=WARNING
handlers=console
[logger_myapp]
level=DEBUG
handlers=console,file
qualname=myapp
propagate=0
[handler_console]
class=StreamHandler
level=INFO
formatter=simple
args=(sys.stdout,)
[handler_file]
class=FileHandler
level=DEBUG
formatter=standard
args=('app.log', 'a', 'utf-8')
[formatter_standard]
format=%(asctime)s [%(levelname)s] %(name)s: %(message)s
[formatter_simple]
format=%(levelname)s: %(message)s# 使用配置文件
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('myapp')
logger.info("使用配置文件")7. 实用技巧
7.1 异常信息记录
import logging
logging.basicConfig(level=logging.ERROR)
try:
1 / 0
except Exception:
# 自动记录异常 traceback
logging.exception("发生错误")
# 输出包含完整的堆栈信息7.2 自定义过滤器
import logging
class LevelFilter(logging.Filter):
"""只允许指定级别的日志"""
def __init__(self, level):
self.level = level
def filter(self, record):
return record.levelno == self.level
# 使用
logger = logging.getLogger('filtered')
handler = logging.StreamHandler()
handler.addFilter(LevelFilter(logging.WARNING))
logger.addHandler(handler)7.3 上下文信息
import logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('context')
# 使用 extra 添加上下文
logger.info('用户操作', extra={'user_id': '12345'})本节小结
- 级别:DEBUG < INFO < WARNING < ERROR < CRITICAL
- 记录器:通过
getLogger(name)获取,支持层次结构 - 处理器:
StreamHandler(控制台)、FileHandler(文件)、RotatingFileHandler(轮转) - 格式化器:控制日志输出格式,支持多种字段
- 配置方式:代码配置、字典配置(推荐)、配置文件
- 最佳实践:使用模块级记录器,使用
__name__作为记录器名
练习
- 配置日志同时输出到控制台(INFO+)和文件(DEBUG+),文件按天轮转
- 编写一个装饰器,自动记录函数的入参、出参和执行时间
- 创建一个自定义处理器,将 ERROR 以上级别的日志发送到邮件
- 实现一个日志过滤器,只记录包含特定关键词的日志