Python日志系统实战指南:深入理解Logger模块与功能
Python日志系统详解:Logger完全指南
目录
1. Logger简介
Logger是Python内置模块logging中的一个重要组件,用来记录程序运行过程中的各种事件,比如错误、警告、信息、调试信息等等。
简单说:
为什么要用Logger而不是直接print?
2. Logger的基本组成和流程
Logger系统主要由四个部分组成:
组件 | 作用 |
---|---|
Logger | 日志记录器,产生日志 |
Handler | 处理器,决定日志往哪里输出(屏幕?文件?) |
Formatter | 格式器,规定日志长什么样(时间?级别?内容?) |
Filter(可选) | 过滤器,筛选哪些日志能过 |
简单理解为:
Logger产生日志 → Handler接收日志 → Formatter美化日志 → 输出到你想要的地方。
3. Logger对象详解
Logger对象就是"日志生产者",专门负责产生日志消息。
每个Logger都有:
创建Logger的方法:
import logging
# 创建Logger对象
logger = logging.getLogger('my_logger')
# 可以随便起名字,推荐用当前模块名(如 __name__)
设置Logger的日志级别:
logger.setLevel(logging.DEBUG) # 设为DEBUG,记录所有等级的日志
Logger的常用方法:
logger.debug('这是调试信息')
logger.info('这是普通信息')
logger.warning('这是警告信息')
logger.error('这是错误信息')
logger.critical('这是严重错误信息')
logger.exception('这是捕获异常信息,包含堆栈') # 通常在except块中使用
4. Handler对象详解
Handler对象是"日志搬运工",负责把日志消息送到指定地方。
常用的Handler类型:
Handler类型 | 作用 |
---|---|
StreamHandler | 输出到控制台(标准输出) |
FileHandler | 输出到文件 |
RotatingFileHandler | 输出到文件,支持按大小切割 |
TimedRotatingFileHandler | 输出到文件,支持按时间切割 |
SMTPHandler | 输出到邮件 |
SysLogHandler | 输出到系统日志 |
HTTPHandler | 输出到HTTP服务器 |
每个Handler也可以设置自己的日志级别(比如只接收ERROR级别以上的日志)。
创建Handler的例子:
# 输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
# 输出到文件
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.ERROR)
# 输出到按大小切割的文件(最多10MB,备份5个文件)
rotating_handler = logging.handlers.RotatingFileHandler(
'app.log', maxBytes=10*1024*1024, backupCount=5
)
# 按时间切割(每天午夜切割,保留7天)
timed_handler = logging.handlers.TimedRotatingFileHandler(
'app.log', when='midnight', interval=1, backupCount=7
)
5. Formatter详解
Formatter就是"日志化妆师",负责给日志加上漂亮的格式。
格式化占位符:
占位符 | 含义 |
---|---|
%(asctime)s | 日期时间,如:2023-01-01 12:00:00,123 |
%(name)s | 记录器名称 |
%(levelname)s | 日志级别名称 |
%(levelno)s | 日志级别数值 |
%(pathname)s | 文件的完整路径 |
%(filename)s | 文件名 |
%(module)s | 模块名 |
%(funcName)s | 函数名 |
%(lineno)d | 行号 |
%(message)s | 日志消息内容 |
%(process)d | 进程ID |
%(processName)s | 进程名称 |
%(thread)d | 线程ID |
%(threadName)s | 线程名称 |
创建Formatter例子:
# 一般格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 详细格式(带文件名、行号、函数名)
detailed_formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] %(filename)s:%(lineno)d %(funcName)s() - %(message)s'
)
# 精确时间格式(带毫秒)
precise_formatter = logging.Formatter(
'%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
设置Formatter到Handler:
console_handler.setFormatter(formatter)
file_handler.setFormatter(detailed_formatter)
6. 完整实例(快速搭建一个Logger)
下面这段代码,一次性串联了完整流程:
import logging
# 1. 创建Logger对象
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG) # 设置日志级别
# 2. 创建Handler
console_handler = logging.StreamHandler()
file_handler = logging.FileHandler('app.log')
# 3. 设置Handler的日志级别
console_handler.setLevel(logging.DEBUG)
file_handler.setLevel(logging.ERROR)
# 4. 创建Formatter并绑定到Handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 5. 将Handler添加到Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 6. 写日志
logger.debug('调试信息')
logger.info('普通信息')
logger.warning('警告信息')
logger.error('错误信息')
logger.critical('严重错误信息')
效果:
7. Logger日志级别详细解释
等级 | 数值 | 说明 | 使用场景 |
---|---|---|---|
DEBUG | 10 | 调试细节 | 开发阶段,详细追踪问题 |
INFO | 20 | 普通流程信息 | 常规操作,程序运行状态 |
WARNING | 30 | 警告(程序还能跑,但可能出问题) | 需要注意但不是错误的情况 |
ERROR | 40 | 错误(程序某些功能无法继续) | 运行出错,但不会导致崩溃 |
CRITICAL | 50 | 严重错误(整个程序可能崩掉) | 紧急情况,程序无法继续运行 |
Logger只会处理大于等于设定等级的日志信息。
重要提示:默认Logger等级是WARNING,所以如果不设置,DEBUG和INFO是不会显示的!
8. 更高级的Logger配置方法
除了在代码里一行行设置,还可以用配置文件或者字典集中管理Logger,非常适合大项目。
8.1 使用配置文件(INI格式)
创建一个logging.conf:
[loggers]
keys=root,myLogger
[handlers]
keys=consoleHandler,fileHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=WARNING
handlers=consoleHandler
[logger_myLogger]
level=DEBUG
handlers=consoleHandler,fileHandler
qualname=myLogger
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=ERROR
formatter=simpleFormatter
args=('app.log', 'a')
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
然后在代码中这样用:
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('myLogger')
logger.debug('调试信息')
8.2 使用字典配置(推荐!)
直接在代码里用字典管理所有设置:
import logging.config
logging_config = {
'version': 1,
'formatters': {
'simple': {'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'simple',
'level': 'DEBUG',
},
'file': {
'class': 'logging.FileHandler',
'formatter': 'simple',
'level': 'ERROR',
'filename': 'app.log'
},
},
'loggers': {
'myLogger': {
'handlers': ['console', 'file'],
'level': 'DEBUG',
'propagate': False,
}
}
}
logging.config.dictConfig(logging_config)
logger = logging.getLogger('myLogger')
logger.info('通过字典配置输出日志')
# getLogger参数可以是任意字符串,不一定要在配置字典里定义。如果配置了就用配置,否则用默认规则。
8.3 使用YAML配置(更易读)
如果项目支持PyYAML,可以用YAML格式配置:
version: 1
formatters:
simple:
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: simple
file:
class: logging.FileHandler
level: ERROR
formatter: simple
filename: app.log
loggers:
myLogger:
level: DEBUG
handlers: [console, file]
propagate: false
使用方法:
import yaml
import logging.config
with open('logging.yaml', 'r') as f:
config = yaml.safe_load(f)
logging.config.dictConfig(config)
logger = logging.getLogger('myLogger')
9. Logger使用小技巧(最佳实践)
9.1 分模块使用Logger
每个模块用自己的Logger,别全靠root logger:
# 在 user_service.py 中
logger = logging.getLogger(__name__) # 'user_service'
# 在 order_service.py 中
logger = logging.getLogger(__name__) # 'order_service'
9.2 合理设置日志级别
可以通过环境变量动态调整:
import os
import logging
log_level = os.environ.get('LOG_LEVEL', 'WARNING').upper()
numeric_level = getattr(logging, log_level, logging.WARNING)
logger.setLevel(numeric_level)
9.3 使用日志轮转
防止单个日志文件太大或太多:
import logging
from logging.handlers import RotatingFileHandler
# 限制每个文件最大10MB,最多5个备份
handler = RotatingFileHandler('app.log', maxBytes=10*1024*1024, backupCount=5)
9.4 记录异常信息
使用logger.exception自动记录堆栈信息:
try:
1 / 0
except Exception as e:
logger.exception('除零错误,完整堆栈:')
# 等价于:logger.error('除零错误,完整堆栈:', exc_info=True)
9.5 添加上下文信息
使用LoggerAdapter或Filter添加自定义字段:
class RequestIdFilter(logging.Filter):
def __init__(self, request_id):
super().__init__()
self.request_id = request_id
def filter(self, record):
record.request_id = self.request_id
return True
# 使用方法
request_filter = RequestIdFilter(request_id='12345')
handler.addFilter(request_filter)
formatter = logging.Formatter('%(asctime)s [%(request_id)s] %(levelname)s: %(message)s')
9.6 性能优化
日志可能影响性能,特别是DEBUG级别,可以使用懒加载方式:
# 不好的方式(即使不输出也会计算字符串)
logger.debug("User data: " + str(large_user_data))
# 好的方式(只有真正需要输出时才计算)
logger.debug("User data: %s", large_user_data)
# 更好的方式(进一步避免不必要的计算)
if logger.isEnabledFor(logging.DEBUG):
logger.debug("User data: %s", large_user_data)
10. 常见问题与解决方案
10.1 为什么我的日志没有显示?
可能原因:
解决方法:
# 检查Logger级别
logger.setLevel(logging.DEBUG)
# 添加控制台Handler(最基本的显示方式)
if not logger.handlers:
handler = logging.StreamHandler()
logger.addHandler(handler)
# 禁止向父Logger传播
logger.propagate = False
10.2 如何在多线程/多进程环境中安全使用Logger?
Python的logging模块默认是线程安全的,但多进程需要注意:
# 多进程安全的文件日志配置
handler = logging.FileHandler('app.log', delay=True)
handler.setFormatter(logging.Formatter('%(asctime)s - %(process)d - %(message)s'))
# 更好的方式:使用QueueHandler和QueueListener
import queue
from logging.handlers import QueueHandler, QueueListener
log_queue = queue.Queue()
queue_handler = QueueHandler(log_queue)
logger.addHandler(queue_handler)
# 在主进程中启动监听器
file_handler = logging.FileHandler('app.log')
listener = QueueListener(log_queue, file_handler)
listener.start()
# 程序结束时停止
listener.stop()
10.3 如何在Django/Flask等框架中使用Logger?
大多数框架已有内置Logger配置,可以直接使用或扩展:
Django:
import logging
logger = logging.getLogger(__name__)
# 在settings.py中配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': 'debug.log',
},
},
'loggers': {
'my_app': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
Flask:
from flask.logging import default_handler
app.logger.removeHandler(default_handler)
handler = logging.FileHandler('flask_app.log')
app.logger.addHandler(handler)
11. 总结
Python的日志系统设计精妙,掌握了它,你就能让程序清晰地"说话",无论是开发调试还是生产环境监控,都能事半功倍。
参考资源
作者:于归pro