python logging模块完整教程
python的logging模块是标准库中强大而灵活的日志记录工具,它能够帮助开发者记录应用程序运行过程中的各种事件和信息。通过本教程,您将从基础概念开始,逐步学习如何配置、使用和优化日志系统,最终掌握在实际项目中应用logging模块的各种技巧。本教程涵盖了从简单的日志输出到高级的日志配置和性能优化的完整内容。
什么是 python logging
python logging模块是python标准库的一部分,提供了一个灵活的框架来记录应用程序运行时的各种事件和信息。
核心特点
- 层次化日志系统: 支持多个logger形成层次结构
- 多种日志级别: debug、info、warning、error、critical
- 灵活的输出目标: 控制台、文件、网络等
- 可配置的格式: 自定义日志消息格式
- 线程安全: 支持多线程环境
- 性能优化: 支持异步日志处理
快速开始
学习建议
建议先跟着这个例子动手实践,理解基本流程后再深入学习细节。
hello world 示例
import logging
# 基本的日志记录
logging.debug("这是一条调试信息")
logging.info("这是一条信息")
logging.warning("这是一条警告")
logging.error("这是一条错误")
logging.critical("这是一条严重错误")代码说明:
import logging: 导入logging模块logging.debug(): 记录调试级别信息logging.info(): 记录普通信息logging.warning(): 记录警告信息logging.error(): 记录错误信息logging.critical(): 记录严重错误信息
运行结果:
warning:root:这是一条警告
error:root:这是一条错误
critical:root:这是一条严重错误
代码解析
- 默认配置: 默认情况下,只有warning及以上级别的日志会被显示
- 默认格式:
[级别]:[logger名称]:[消息] - 默认输出: 默认输出到stderr(标准错误流)
[!question] 为什么要这样写?
logging模块的默认配置相对保守,只显示warning级别的日志。这是因为过多的日志信息可能会干扰程序的主要输出,同时日志系统的开销也应该被控制。
[!bug] 如果没有看到debug和info日志
这是正常的!logging默认只显示warning及以上级别的日志。要显示所有级别的日志,需要配置logging级别。
核心概念
概念1:logger(记录器)
定义: logger是日志系统的核心组件,用于创建和记录日志消息。
为什么重要: logger为应用程序提供了记录日志的入口点,它决定了哪些级别的消息会被记录,以及消息应该发送到哪里。
工作原理:
logger创建后会被分配一个名称和日志级别。当调用logger的日志方法时,会检查消息的级别是否高于或等于logger的级别,如果是则处理该消息。
示例:
import logging
# 创建logger
logger = logging.getlogger('my_app')
logger.setlevel(logging.debug)
# 记录日志
logger.debug("调试消息")
logger.info("信息消息")
logger.warning("警告消息")[!note] 深入理解
- logger名称通常采用点分格式,如’my_app.database’
- 同一个名称的logger返回的是同一个实例
- 如果不设置级别,会继承父logger的级别
概念2:handler(处理器)
定义: handler负责将日志消息发送到目标位置,如控制台、文件、网络等。
为什么重要: handler决定了日志的最终去向,是实现日志系统灵活性的关键。
工作原理:
每个logger可以附加多个handler。当logger记录消息时,会将消息传递给所有附加的handler,每个handler根据其配置独立处理消息。
示例:
import logging
# 创建logger
logger = logging.getlogger('my_app')
logger.setlevel(logging.debug)
# 控制台handler
console_handler = logging.streamhandler()
console_handler.setlevel(logging.info)
# 文件handler
file_handler = logging.filehandler('app.log')
file_handler.setlevel(logging.debug)
# 添加handler
logger.addhandler(console_handler)
logger.addhandler(file_handler)
# 记录日志
logger.debug("只记录到文件")
logger.info("同时记录到控制台和文件")[!note] 深入理解
- 一个logger可以关联多个handler
- 每个handler可以有自己的日志级别和格式
- 可以通过handler实现日志过滤、分发等功能
概念3:formatter(格式化器)
定义: formatter定义了日志消息的输出格式。
为什么重要: 好的日志格式能够使日志信息更加清晰、易读,便于问题排查。
工作原理:
formatter将日志事件对象转换为字符串。可以自定义包含时间戳、日志级别、logger名称、消息等字段。
示例:
import logging
# 创建formatter
formatter = logging.formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 创建handler并设置formatter
handler = logging.streamhandler()
handler.setformatter(formatter)
# 使用handler
logger = logging.getlogger('my_app')
logger.addhandler(handler)
# 记录日志
logger.info("格式化的日志消息")输出:
2024-01-01 12:00:00,123 - my_app - info - 格式化的日志消息
[!note] 深入理解
%(asctime)s: 时间戳%(name)s: logger名称%(levelname)s: 日志级别%(message)s: 日志消息- 可以添加更多字段,如文件名、行号、函数名等
概念之间的关系


logger作为入口点,通过日志级别控制哪些消息会被记录。logger可以关联多个handler,每个handler可以有自己的formatter来格式化输出。这样实现了日志系统的灵活配置。
基础功能详解
功能1:日志级别
用途: 控制哪些信息应该被记录,帮助过滤日志信息。
基本语法:
logging.debug("消息") # 调试级别
logging.info("消息") # 信息级别
logging.warning("消息") # 警告级别
logging.error("消息") # 错误级别
logging.critical("消息") # 严重错误级别参数说明:
message: 日志消息字符串,可以包含格式化占位符
简单示例:
import logging
# 设置日志级别
logging.basicconfig(level=logging.debug)
logging.debug("调试信息:程序开始执行")
logging.info("信息:加载配置文件")
logging.warning("警告:配置文件版本可能过时")
logging.error("错误:无法连接到数据库")
logging.critical("严重错误:系统即将崩溃")常见用法:
import logging
# 获取logger
logger = logging.getlogger('app')
# 设置日志级别
logger.setlevel(logging.info) # 只记录info及以上级别
# 针对不同级别有不同处理
if some_critical_condition:
logger.critical("关键问题发生,需要立即处理")
elif some_error_condition:
logger.error("发生错误,影响功能正常运行")
else:
logger.info("系统运行正常")[!tip] 最佳实践
- 在开发环境中使用debug级别记录详细信息
- 在生产环境中使用info或warning级别避免日志过多
- 根据重要性选择合适的日志级别
[!warning] 常见陷阱
- 不要过度使用debug级别,否则会产生大量无用日志
- 避免在循环中频繁记录info级别日志,影响性能
- 确保错误信息包含足够的上下文信息
功能2:基本配置
用途: 快速配置logging系统的基本参数。
基本语法:
logging.basicconfig(
level=logging.debug,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='app.log',
filemode='a'
)
参数说明:
level: 日志级别阈值format: 日志消息格式filename: 日志文件名filemode: 文件打开模式('w’覆盖,'a’追加)datefmt: 日期时间格式style: 格式样式(‘%’, ‘{’, ‘$’)force: 是否重新配置已经存在的logging
简单示例:
import logging
# 基本配置
logging.basicconfig(
level=logging.debug,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='my_app.log',
filemode='a'
)
# 现在所有日志都会写入文件
logging.debug("调试信息")
logging.info("信息")常见用法:
import logging
# 生产环境配置
logging.basicconfig(
level=logging.warning,
format='%(asctime)s [%(levelname)s] %(message)s',
filename='production.log'
)
# 开发环境配置
import sys
logging.basicconfig(
level=logging.debug,
format='%(asctime)s %(name)s %(levelname)s: %(message)s',
stream=sys.stdout # 输出到控制台
)[!tip] 最佳实践
- 在程序入口处进行一次basicconfig配置
- 根据环境(开发/测试/生产)调整日志级别
- 使用不同的输出目标(控制台/文件)进行调试
[[文件操作]]
功能3:logger创建和使用
用途: 创建和管理应用程序的logger实例。
基本语法:
logger = logging.getlogger('logger_name')
logger.debug("消息")
参数说明:
logger_name: logger的名称,建议使用模块名或类名
简单示例:
import logging
# 创建不同模块的logger
db_logger = logging.getlogger('my_app.database')
web_logger = logging.getlogger('my_app.web')
user_logger = logging.getlogger('my_app.user')
# 配置logger级别
db_logger.setlevel(logging.debug)
web_logger.setlevel(logging.info)
user_logger.setlevel(logging.warning)
# 记录日志
db_logger.debug("数据库连接建立")
db_logger.info("执行sql查询")
web_logger.info("收到http请求")
user_logger.warning("用户尝试访问无权限的页面")常见用法:
import logging
import os
# 基于模块名创建logger
logger = logging.getlogger(__name__)
# 如果logger还没有配置,进行基本配置
if not logger.handlers:
handler = logging.streamhandler()
formatter = logging.formatter(
'%(asctime)s [%(levelname)s] %(name)s: %(message)s'
)
handler.setformatter(formatter)
logger.addhandler(handler)
logger.setlevel(logging.info)
# 使用logger
logger.debug("debug message")
logger.info("app started")
logger.error("failed to process request")[!tip] 最佳实践
- 使用
__name__作为logger名称,这样能清楚地知道日志来源 - 每个模块创建自己的logger实例
- 避免直接使用logging模块的函数,而是使用logger实例
功能4:handler配置
用途: 配置日志输出的目标位置和方式。
基本语法:
# streamhandler - 输出到控制台
console_handler = logging.streamhandler()
# filehandler - 输出到文件
file_handler = logging.filehandler('app.log')
# rotatingfilehandler - 轮转文件
from logging.handlers import rotatingfilehandler
rotating_handler = rotatingfilehandler(
'app.log',
maxbytes=1024*1024, # 1mb
backupcount=5
)
# timedrotatingfilehandler - 按时间轮转
from logging.handlers import timedrotatingfilehandler
timed_handler = timedrotatingfilehandler(
'app.log',
when='midnight',
backupcount=7
)参数说明:
streamhandler: 流处理器,可以指定输出流(默认是stderr)filehandler: 文件处理器maxbytes: 文件最大大小(rotatingfilehandler)backupcount: 保留的备份文件数when: 轮转时间间隔(timedrotatingfilehandler)
简单示例:
import logging
from logging.handlers import rotatingfilehandler
# 创建logger
logger = logging.getlogger('app')
logger.setlevel(logging.debug)
# 控制台handler
console_handler = logging.streamhandler()
console_handler.setlevel(logging.info)
# 文件handler
file_handler = rotatingfilehandler(
'app.log',
maxbytes=1024*1024, # 1mb
backupcount=3,
encoding='utf-8'
)
file_handler.setlevel(logging.debug)
# 添加handler
logger.addhandler(console_handler)
logger.addhandler(file_handler)
# 测试日志
logger.debug("这是调试信息,只记录到文件")
logger.info("这是信息,同时记录到控制台和文件")常见用法:
import logging
import sys
from logging.handlers import smtphandler
# 配置多输出
logger = logging.getlogger('my_app')
# 控制台输出
console = logging.streamhandler(sys.stdout)
console.setlevel(logging.info)
console_format = logging.formatter('%(levelname)s: %(message)s')
console.setformatter(console_format)
# 文件输出
file_handler = logging.filehandler('app.log')
file_handler.setlevel(logging.debug)
file_format = logging.formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setformatter(file_format)
# 邮件handler(仅用于严重错误)
mail_handler = smtphandler(
mailhost=('smtp.example.com', 587),
fromaddr='app@example.com',
toaddrs=['admin@example.com'],
subject='application error',
credentials=('username', 'password'),
secure=()
)
mail_handler.setlevel(logging.critical)
# 添加handler
logger.addhandler(console)
logger.addhandler(file_handler)
logger.addhandler(mail_handler)[!tip] 最佳实践
- 为不同的日志级别使用不同的输出渠道
- 生产环境中配置适当的轮转策略,避免日志文件过大
- 使用不同的格式满足不同的输出需求
[[异常处理]]
[[python基础语法]]
实践项目
[!example] 项目目标
通过构建一个简单的web服务器日志系统,综合运用前面学到的logging知识。
项目需求
- 记录http请求信息
- 根据请求状态分类日志级别
- 实现日志轮转和归档
- 支持多种输出格式
项目结构
web_logger/
├── main.py
├── logger_config.py
├── requirements.txt
└── logs/
└── app.log
实现步骤
步骤1:创建日志配置模块
目标:创建一个可复用的日志配置模块。
代码:
# logger_config.py
import logging
import logging.handlers
import os
from datetime import datetime
class webloggerconfig:
def __init__(self, log_dir='logs'):
self.log_dir = log_dir
self.logger = logging.getlogger('web_server')
self._setup_logger()
def _setup_logger(self):
# 确保日志目录存在
os.makedirs(self.log_dir, exist_ok=true)
# 设置logger级别
self.logger.setlevel(logging.debug)
# 清除现有的handler
self.logger.handlers.clear()
# 创建formatter
detailed_formatter = logging.formatter(
'%(asctime)s [%(levelname)s] %(name)s: %(message)s'
)
# 控制台handler
console_handler = logging.streamhandler()
console_handler.setlevel(logging.info)
console_handler.setformatter(detailed_formatter)
# 文件handler - 主日志
file_handler = logging.handlers.rotatingfilehandler(
os.path.join(self.log_dir, 'app.log'),
maxbytes=10*1024*1024, # 10mb
backupcount=5,
encoding='utf-8'
)
file_handler.setlevel(logging.debug)
file_handler.setformatter(detailed_formatter)
# 错误日志handler
error_handler = logging.handlers.rotatingfilehandler(
os.path.join(self.log_dir, 'error.log'),
maxbytes=5*1024*1024, # 5mb
backupcount=3,
encoding='utf-8'
)
error_handler.setlevel(logging.error)
error_handler.setformatter(detailed_formatter)
# 添加所有handler
self.logger.addhandler(console_handler)
self.logger.addhandler(file_handler)
self.logger.addhandler(error_handler)
def get_logger(self):
return self.logger解释:
- 创建了webloggerconfig类来管理日志配置
- 实现了多级日志输出(控制台、主文件、错误文件)
- 使用rotatingfilehandler实现日志轮转
- 错误日志单独记录,便于问题排查
步骤2:创建主应用程序
目标:模拟web服务器处理http请求并记录日志。
代码:
# main.py
from logger_config import webloggerconfig
import random
import time
from datetime import datetime
# 初始化日志系统
logger_config = webloggerconfig()
logger = logger_config.get_logger()
def simulate_web_request(request_id):
"""模拟处理http请求"""
logger.info(f"收到请求 #{request_id}")
# 模拟请求处理时间
process_time = random.uniform(0.1, 2.0)
time.sleep(process_time)
# 模拟不同的响应状态
status_code = random.choices(
[200, 404, 500],
weights=[0.8, 0.15, 0.05]
)[0]
# 记录处理结果
if status_code == 200:
logger.debug(f"请求 #{request_id} 处理成功,耗时: {process_time:.2f}s")
logger.info(f"响应 #{request_id}: {status_code} ok")
return "ok"
elif status_code == 404:
logger.warning(f"请求 #{request_id}: 404 not found - 资源不存在")
return "not found"
else:
logger.error(f"请求 #{request_id} 处理失败,错误代码: {status_code}")
logger.error(f"详细信息: 服务器内部错误")
return "internal server error"
def main():
"""主程序"""
logger.info("web服务器启动")
logger.info("开始处理请求...")
try:
# 模拟处理100个请求
for i in range(1, 101):
result = simulate_web_request(i)
# 每10个请求输出统计信息
if i % 10 == 0:
logger.info(f"已处理 {i} 个请求")
except keyboardinterrupt:
logger.warning("收到中断信号,正在关闭服务器...")
except exception as e:
logger.critical(f"服务器发生严重错误: {str(e)}", exc_info=true)
finally:
logger.info("web服务器关闭")
if __name__ == "__main__":
main()解释:
- 使用webloggerconfig获取配置好的logger
- 实现了模拟http请求处理的函数
- 根据响应状态码使用不同的日志级别
- 添加了异常处理和程序生命周期日志
完整代码
项目的完整代码包含两个文件:
logger_config.py- 日志配置模块main.py- 主应用程序
运行和测试
# 安装依赖(这个项目不需要额外依赖) pip install -r requirements.txt # 运行程序 python main.py
预期输出:
2024-01-01 10:00:00,123 [info] web_server: web服务器启动
2024-01-01 10:00:00,124 [info] web_server: 开始处理请求...
2024-01-01 10:00:00,125 [info] web_server: 收到请求 #1
2024-01-01 10:00:01,321 [info] web_server: 响应 #1: 200 ok
2024-01-01 10:00:01,322 [info] web_server: 收到请求 #2
2024-01-01 10:00:02,456 [warning] web_server: 请求 #2: 404 not found - 资源不存在
...
2024-01-01 10:00:15,789 [info] web_server: 已处理 10 个请求
...
检查logs目录下的日志文件,可以看到:
app.log- 包含所有日志error.log- 只包含error级别的日志
进阶主题
主题1:自定义日志级别
什么时候需要: 当内置的日志级别不足以满足业务需求时,如需要区分不同类型的警告。
深入理解:
python logging允许创建自定义的日志级别,这样可以根据业务需求定义更细粒度的日志分类。自定义级别需要定义级别数值和级别名称。
高级示例:
import logging
# 定义自定义级别
logging.addlevelname(35, "trace") # 比debug更低的级别
logging.addlevelname(45, "notice") # 介于info和warning之间
def trace(self, message, *args, **kwargs):
"""trace级别方法"""
if self.isenabledfor(35):
self._log(35, message, args, **kwargs)
def notice(self, message, *args, **kwargs):
"""notice级别方法"""
if self.isenabledfor(45):
self._log(45, message, args, **kwargs)
# 添加方法到logger类
logging.logger.trace = trace
logging.logger.notice = notice
# 使用自定义级别
logger = logging.getlogger('app')
logger.trace("详细的跟踪信息")
logger.debug("调试信息")
logger.notice("需要注意的事项")
logger.info("常规信息")性能考虑:
- 自定义级别会增加系统开销
- 合理设置级别数值,避免与现有级别冲突
- 考虑业务需求是否真的需要额外的级别
主题2:上下文感知的日志
什么时候需要: 需要在日志中包含请求id、用户id等上下文信息时。
深入理解:
通过使用logger的adapter功能,可以轻松地为所有日志消息添加上下文信息,这样在分布式系统中可以追踪完整的请求链路。
高级示例:
import logging
class contextadapter(logging.loggeradapter):
"""带上下文的logger适配器"""
def __init__(self, logger, extra=none):
super().__init__(logger, extra or {})
def process(self, msg, kwargs):
# 为每条消息添加上下文
kwargs['extra'] = kwargs.get('extra', {})
kwargs['extra'].update(self.extra)
return msg, kwargs
# 使用示例
logger = logging.getlogger('web_service')
adapter = contextadapter(logger, {
'request_id': 'req-12345',
'user_id': 'user-67890',
'ip_address': '192.168.1.1'
})
# 记录日志
adapter.info("用户登录成功")
adapter.error("数据库连接失败")
# 输出示例:
# 2024-01-01 12:00:00 [info] web_service: 用户登录成功
# request_id=req-12345 user_id=user-67890 ip_address=192.168.1.1性能考虑:
- 上下文信息会占用内存
- 避免在上下文中存储大量数据
- 考虑使用线程局部变量存储上下文
主题3:异步日志
什么时候需要: 在高并发环境下,同步日志记录可能成为性能瓶颈时。
深入理解:
异步日志通过后台线程处理日志写入,避免阻塞主线程。python 3.7+的logging模块内置了queuehandler和queuelistener来实现异步日志。
高级示例:
import logging
import logging.handlers
import queue
import time
from threading import thread
class asynclogging:
def __init__(self):
self.log_queue = queue.queue(-1) # 无限队列
self.setup_logger()
def setup_logger(self):
# 配置logger
logger = logging.getlogger('async_app')
logger.setlevel(logging.debug)
# 创建queuehandler
queue_handler = logging.handlers.queuehandler(self.log_queue)
logger.addhandler(queue_handler)
# 创建queuelistener
file_handler = logging.filehandler('async_app.log')
file_handler.setformatter(logging.formatter(
'%(asctime)s - %(levelname)s - %(message)s'
))
# 启动监听器线程
self.listener = logging.handlers.queuelistener(
self.log_queue,
file_handler,
respect_handler_level=true
)
self.listener.start()
# 保存logger
self.logger = logger
def stop(self):
"""停止异步日志"""
self.logger.info("正在关闭异步日志系统")
self.listener.stop()
def get_logger(self):
return self.logger
# 使用异步日志
async_logger = asynclogging()
logger = async_logger.get_logger()
# 模拟高并发场景
def worker(worker_id):
for i in range(10):
logger.info(f"worker {worker_id} 处理任务 {i}")
time.sleep(0.1)
# 创建多个线程
threads = []
for i in range(5):
t = thread(target=worker, args=(i,))
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
# 停止异步日志
async_logger.stop()性能考虑:
- 异步日志会增加内存使用(队列)
- 需要合理设置队列大小,避免内存溢出
- 程序退出时需要正确关闭监听器
- 在高吞吐量场景下,异步日志能显著提升性能
主题4:结构化日志
什么时候需要: 需要将日志数据发送到日志分析系统(如elk stack)时。
深入理解:
结构化日志使用json等格式记录日志,便于日志解析和分析。python 3.9+的logging模块支持直接输出json格式的日志。
高级示例:
import logging
import json
from datetime import datetime
class jsonformatter(logging.formatter):
"""json格式的日志格式化器"""
def format(self, record):
log_data = {
'timestamp': datetime.fromtimestamp(record.created).isoformat(),
'level': record.levelname,
'logger': record.name,
'message': record.getmessage(),
'module': record.module,
'function': record.funcname,
'line': record.lineno,
'thread': record.thread,
'process': record.process
}
# 添加异常信息
if record.exc_info:
log_data['exception'] = self.formatexception(record.exc_info)
# 添加额外的字段
if hasattr(record, 'extra_data'):
log_data.update(record.extra_data)
return json.dumps(log_data, ensure_ascii=false)
# 配置结构化日志
logger = logging.getlogger('structured')
logger.setlevel(logging.debug)
# 文件handler
file_handler = logging.filehandler('structured.log')
file_handler.setformatter(jsonformatter())
# 控制台handler
console_handler = logging.streamhandler()
console_handler.setformatter(logging.formatter(
'%(levelname)s: %(message)s'
))
logger.addhandler(file_handler)
logger.addhandler(console_handler)
# 记录结构化日志
logger.info("用户登录", extra={
'user_id': '12345',
'ip': '192.168.1.1',
'device': 'mobile'
})
logger.error("数据库操作失败", exc_info=true)输出示例(json):
{
"timestamp": "2024-01-01t12:00:00.123456",
"level": "info",
"logger": "structured",
"message": "用户登录",
"module": "main",
"function": "main",
"line": 42,
"thread": 12345,
"process": 67890,
"user_id": "12345",
"ip": "192.168.1.1",
"device": "mobile"
}
到此这篇关于python logging模块详细教程与最佳实践的文章就介绍到这了,更多相关python logging模块内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论