python中logging库的详细用法
Python logging 库详细指南
logging 是 Python 标准库中功能强大且灵活的日志记录模块。下面详细介绍其用法。
1. 基础用法
快速开始
import logging
# 基础配置和简单使用
logging.basicConfig(level=logging.DEBUG)
logging.debug('这是 debug 信息')
logging.info('这是 info 信息')
logging.warning('这是 warning 信息')
logging.error('这是 error 信息')
logging.critical('这是 critical 信息')
日志级别
import logging
# 日志级别从低到高
"""
CRITICAL = 50
ERROR = 40
WARNING = 30
INFO = 20
DEBUG = 10
NOTSET = 0
"""
# 设置日志级别
logging.basicConfig(level=logging.INFO) # 只记录 INFO 及以上级别的日志
logging.debug('这条不会显示') # 级别 10 < 20,不记录
logging.info('这条会显示') # 级别 20 >= 20,记录
logging.warning('这条也会显示') # 级别 30 >= 20,记录
2. 高级配置
basicConfig 详细参数
import logging
import sys
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
handlers=[
logging.FileHandler('app.log', encoding='utf-8'),
logging.StreamHandler(sys.stdout)
]
)
logging.info('配置完成的日志记录')
格式化字符串常用字段
"""
%(name)s Logger 的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用 UNIX 标准的表示时间的浮点数表示
%(asctime)s 字符串形式的当前时间
%(msecs)d 毫秒部分
%(relativeCreated)d 日志事件发生的时间相对于 logging 模块加载时间的相对毫秒数
%(thread)d 线程 ID
%(threadName)s 线程名
%(process)d 进程 ID
%(message)s 用户输出的消息
"""
3. Logger, Handler, Formatter, Filter
完整的组件配置
import logging
import logging.handlers
def setup_logger():
# 创建 logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# 防止日志重复处理
if logger.handlers:
logger.handlers.clear()
# 创建 formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 控制台 handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
# 文件 handler
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
# 滚动日志文件 handler
rotating_handler = logging.handlers.RotatingFileHandler(
'app_rotating.log',
maxBytes=1024*1024, # 1MB
backupCount=5,
encoding='utf-8'
)
rotating_handler.setLevel(logging.DEBUG)
rotating_handler.setFormatter(formatter)
# 时间滚动日志 handler
timed_handler = logging.handlers.TimedRotatingFileHandler(
'app_timed.log',
when='midnight', # 每天午夜
interval=1,
backupCount=7,
encoding='utf-8'
)
timed_handler.setLevel(logging.DEBUG)
timed_handler.setFormatter(formatter)
# 添加所有 handlers
logger.addHandler(console_handler)
logger.addHandler(file_handler)
logger.addHandler(rotating_handler)
logger.addHandler(timed_handler)
return logger
# 使用配置好的 logger
logger = setup_logger()
logger.info('应用程序启动')
logger.debug('调试信息')
logger.error('错误信息')
4. 高级特性
日志过滤器
import logging
class InfoFilter(logging.Filter):
def filter(self, record):
# 只允许 INFO 级别的日志通过
return record.levelno == logging.INFO
# 配置过滤器
logger = logging.getLogger('filtered_logger')
handler = logging.StreamHandler()
handler.addFilter(InfoFilter())
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.debug('DEBUG 信息') # 被过滤
logger.info('INFO 信息') # 显示
logger.warning('WARNING 信息') # 被过滤
异常处理
import logging
import traceback
logger = logging.getLogger('exception_logger')
logging.basicConfig(level=logging.DEBUG)
def risky_function():
try:
1 / 0
except Exception as e:
# 方法1: 使用 logging.exception (自动包含堆栈跟踪)
logger.exception('发生除零错误')
# 方法2: 使用 logger.error 并手动添加堆栈
logger.error('发生错误: %s, 堆栈: %s', e, traceback.format_exc())
# 方法3: 使用 exc_info 参数
logger.error('错误详情:', exc_info=True)
risky_function()
结构化日志 (JSON 格式)
import logging
import json
class JSONFormatter(logging.Formatter):
def format(self, record):
log_entry = {
'timestamp': self.formatTime(record),
'level': record.levelname,
'logger': record.name,
'message': record.getMessage(),
'module': record.module,
'function': record.funcName,
'line': record.lineno
}
if record.exc_info:
log_entry['exception'] = self.formatException(record.exc_info)
return json.dumps(log_entry, ensure_ascii=False)
# 使用 JSON 格式
logger = logging.getLogger('json_logger')
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.info('用户登录', extra={'user_id': 12345, 'ip': '192.168.1.1'})
5. 实际应用示例
项目中的日志配置
import logging
import logging.config
import os
from pathlib import Path
def setup_project_logging():
log_dir = Path('logs')
log_dir.mkdir(exist_ok=True)
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
'detailed': {
'format': '%(asctime)s [%(levelname)s] %(name)s (%(filename)s:%(lineno)d): %(message)s'
},
'json': {
'()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
'format': '%(asctime)s %(levelname)s %(name)s %(message)s'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'standard',
'stream': 'ext://sys.stdout'
},
'file_debug': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'DEBUG',
'formatter': 'detailed',
'filename': log_dir / 'debug.log',
'maxBytes': 10485760, # 10MB
'backupCount': 5,
'encoding': 'utf-8'
},
'file_error': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'ERROR',
'formatter': 'detailed',
'filename': log_dir / 'error.log',
'maxBytes': 10485760,
'backupCount': 5,
'encoding': 'utf-8'
}
},
'loggers': {
'': { # root logger
'handlers': ['console', 'file_debug', 'file_error'],
'level': 'DEBUG',
'propagate': False
},
'my_app': {
'handlers': ['console', 'file_debug'],
'level': 'DEBUG',
'propagate': False
},
'requests': {
'handlers': ['file_error'],
'level': 'WARNING',
'propagate': False
}
}
}
logging.config.dictConfig(LOGGING_CONFIG)
# 初始化日志配置
setup_project_logging()
# 在不同模块中使用
app_logger = logging.getLogger('my_app.main')
db_logger = logging.getLogger('my_app.database')
api_logger = logging.getLogger('my_app.api')
def example_usage():
app_logger.info('应用程序启动')
try:
# 模拟一些操作
db_logger.debug('执行数据库查询: SELECT * FROM users')
api_logger.info('调用外部API')
# 模拟错误
raise ValueError('测试错误')
except Exception as e:
app_logger.error('操作失败', exc_info=True)
app_logger.info('应用程序结束')
example_usage()
上下文信息日志
import logging
from contextlib import contextmanager
class ContextLogger:
def __init__(self, logger, **context):
self.logger = logger
self.context = context
def _add_context(self, msg, *args, **kwargs):
if self.context:
msg = f"{msg} [Context: {self.context}]"
return msg, args, kwargs
def info(self, msg, *args, **kwargs):
msg, args, kwargs = self._add_context(msg, *args, **kwargs)
self.logger.info(msg, *args, **kwargs)
def error(self, msg, *args, **kwargs):
msg, args, kwargs = self._add_context(msg, *args, **kwargs)
self.logger.error(msg, *args, **kwargs)
@contextmanager
def log_context(logger, **context):
"""上下文管理器,为代码块添加日志上下文"""
context_logger = ContextLogger(logger, **context)
context_logger.info("开始执行")
try:
yield context_logger
context_logger.info("执行成功")
except Exception as e:
context_logger.error("执行失败: %s", e)
raise
# 使用示例
logger = logging.getLogger('context_example')
def process_user_data(user_id, action):
with log_context(logger, user_id=user_id, action=action, trace_id='12345'):
logger.info(f"处理用户 {user_id} 的 {action} 操作")
# 模拟处理逻辑
if user_id == 999:
raise Exception("用户不存在")
return f"操作 {action} 完成"
# 测试
process_user_data(123, 'update_profile')
process_user_data(999, 'delete_account') # 这个会失败并记录错误
6. 性能考虑
避免不必要的字符串格式化
import logging
# 不好的写法(即使日志级别不够也会执行字符串格式化)
logger.debug('用户数据: %s', expensive_data_processing())
# 好的写法(先检查日志级别)
if logger.isEnabledFor(logging.DEBUG):
logger.debug('用户数据: %s', expensive_data_processing())
# 或者使用参数形式(推荐)
logger.debug('用户数据: %s', expensive_data_processing()) # 参数在需要时才求值
7. 最佳实践总结
- 使用有意义的 logger 名称:通常使用
__name__ - 合理设置日志级别:生产环境用 INFO,开发环境用 DEBUG
- 使用适当的处理器:控制台用于开发,文件用于生产
- 包含足够的上下文信息:时间、级别、模块、行号等
- 避免敏感信息:不要在日志中记录密码、密钥等
- 使用结构化日志:便于后续分析和处理
- 合理配置日志轮转:避免日志文件过大
这个详细的 logging 指南涵盖了从基础到高级的各个方面,可以帮助你在项目中实现专业级的日志记录。
浙公网安备 33010602011771号