Python的logging库是项目开发中进行有效日志记录的核心工具。通过合理的配置,可以清晰地控制日志级别、格式化输出以及灵活的文件管理,从而提升代码的可维护性和调试效率。下面将详细说明如何配置日志级别、格式和文件输出,并结合具体示例进行演示。
## 1. 日志级别配置
Python logging模块定义了6个标准日志级别(按严重性递增排列)[ref_6]:
| 日志级别 | 数值 | 用途说明 |
|---------|------|---------|
| NOTSET | 0 | 未设置,继承父logger级别 |
| DEBUG | 10 | 调试信息,开发阶段使用 |
| INFO | 20 | 常规信息,确认程序按预期运行 |
| WARNING | 30 | 警告信息,表明可能的问题 |
| ERROR | 40 | 错误信息,程序某功能无法执行 |
| CRITICAL| 50 | 严重错误,程序可能无法继续运行 |
设置日志级别可以过滤掉不需要的日志信息。例如,在开发阶段可设置为DEBUG级别,而在生产环境可设置为WARNING或ERROR级别[ref_2]。
```python
import logging
# 基础配置方式设置日志级别
logging.basicConfig(level=logging.DEBUG) # 设置全局日志级别为DEBUG[ref_2]
# 创建logger并设置级别
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO) # 设置此logger的级别为INFO[ref_3]
# 测试不同级别的日志输出
logger.debug("这条调试信息不会显示,因为级别是INFO")
logger.info("这是一条信息级别的日志")
logger.warning("这是一条警告级别的日志")
logger.error("这是一条错误级别的日志")
```
## 2. 日志格式配置
日志格式决定了每条日志记录的输出样式。通过Formatter组件,可以自定义包含时间、模块名、行号、日志级别和消息内容的格式[ref_4]。
### 2.1 常用格式说明符
| 格式符 | 说明 |
|--------|------|
| %(asctime)s | 日志创建时间 |
| %(name)s | logger名称 |
| %(levelname)s | 日志级别文本 |
| %(message)s | 日志消息内容 |
| %(pathname)s | 调用日志的源文件完整路径 |
| %(filename)s | 文件名 |
| %(module)s | 模块名 |
| %(lineno)d | 调用日志的代码行号 |
| %(funcName)s | 调用日志的函数名 |
| %(process)d | 进程ID |
| %(thread)d | 线程ID |
### 2.2 格式配置示例
```python
import logging
# 创建格式器
formatter = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 创建logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# 创建控制台处理器并设置格式
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# 输出日志
logger.info("应用程序启动成功")
logger.debug("调试信息:变量x的值为42")
```
上述配置将输出类似这样的日志:
```
2023-10-15 14:30:25 - my_app - INFO - app.py:15 - 应用程序启动成功
2023-10-15 14:30:25 - my_app - DEBUG - app.py:16 - 调试信息:变量x的值为42
```
## 3. 文件输出配置
在实际项目中,通常需要将日志保存到文件中以便后续分析。可以通过FileHandler来实现文件输出[ref_2]。
### 3.1 基础文件输出
```python
import logging
# 配置同时输出到控制台和文件
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log', mode='a', encoding='utf-8'), # 追加模式写入文件[ref_2]
logging.StreamHandler() # 输出到控制台
]
)
logger = logging.getLogger(__name__)
logger.info("这条日志会同时输出到控制台和app.log文件")
```
### 3.2 高级文件输出配置
```python
import logging
import logging.handlers
# 创建logger
logger = logging.getLogger('project_logger')
logger.setLevel(logging.DEBUG)
# 创建格式器
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
)
# 1. 按文件大小轮转的处理器(单个文件最大1MB,保留3个备份)
file_handler1 = logging.handlers.RotatingFileHandler(
'app.log',
maxBytes=1024*1024, # 1MB
backupCount=3,
encoding='utf-8'
)
file_handler1.setLevel(logging.INFO)
file_handler1.setFormatter(formatter)
# 2. 按时间轮转的处理器(每天轮转一次,保留7天日志)
file_handler2 = logging.handlers.TimedRotatingFileHandler(
'app_debug.log',
when='midnight', # 每天午夜轮转
interval=1,
backupCount=7,
encoding='utf-8'
)
file_handler2.setLevel(logging.DEBUG)
file_handler2.setFormatter(formatter)
# 3. 控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING) # 控制台只显示WARNING及以上级别
console_handler.setFormatter(formatter)
# 添加所有处理器到logger
logger.addHandler(file_handler1)
logger.addHandler(file_handler2)
logger.addHandler(console_handler)
# 使用示例
logger.debug("详细调试信息") # 只写入app_debug.log
logger.info("用户登录成功") # 写入app.log和app_debug.log
logger.warning("内存使用率较高") # 写入所有文件和控制台
```
## 4. 完整项目配置示例
下面是一个适合中型项目的完整日志配置示例:
```python
import logging
import logging.config
import os
from datetime import datetime
def setup_logging():
"""配置项目日志系统"""
# 创建日志目录
log_dir = 'logs'
if not os.path.exists(log_dir):
os.makedirs(log_dir)
# 生成带时间戳的日志文件名
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
# 日志配置字典(推荐方式)
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'detailed': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
},
'simple': {
'format': '%(levelname)s - %(message)s'
},
'json': {
'format': '{"time": "%(asctime)s", "name": "%(name)s", "level": "%(levelname)s", "module": "%(module)s", "line": %(lineno)d, "message": "%(message)s"}',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'simple',
'stream': 'ext://sys.stdout'
},
'info_file': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'INFO',
'formatter': 'detailed',
'filename': f'{log_dir}/info_{timestamp}.log',
'maxBytes': 10485760, # 10MB
'backupCount': 5,
'encoding': 'utf-8'
},
'error_file': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'ERROR',
'formatter': 'detailed',
'filename': f'{log_dir}/error_{timestamp}.log',
'maxBytes': 10485760,
'backupCount': 5,
'encoding': 'utf-8'
},
'debug_file': {
'class': 'logging.FileHandler',
'level': 'DEBUG',
'formatter': 'json',
'filename': f'{log_dir}/debug_{timestamp}.log',
'mode': 'a',
'encoding': 'utf-8'
}
},
'loggers': {
'': { # 根logger
'handlers': ['console', 'info_file', 'error_file'],
'level': 'DEBUG',
'propagate': True
},
'database': { # 数据库相关日志
'handlers': ['console', 'info_file'],
'level': 'INFO',
'propagate': False
},
'api': { # API相关日志
'handlers': ['console', 'debug_file', 'error_file'],
'level': 'DEBUG',
'propagate': False
}
}
}
# 应用配置
logging.config.dictConfig(LOGGING_CONFIG)
return logging.getLogger(__name__)
# 使用示例
if __name__ == '__main__':
logger = setup_logging()
# 获取不同模块的logger
db_logger = logging.getLogger('database')
api_logger = logging.getLogger('api')
# 记录不同级别的日志
logger.info("应用程序启动")
db_logger.info("数据库连接成功")
api_logger.debug("API请求参数: {'user_id': 123}")
try:
result = 10 / 0
except ZeroDivisionError as e:
logger.error(f"计算错误: {e}", exc_info=True) # 记录异常堆栈
api_logger.error(f"API处理异常: {e}")
```
## 5. 最佳实践建议
1. **模块化日志管理**:为不同模块创建独立的logger,如`logging.getLogger('module_name')`,便于分类管理[ref_3]。
2. **合理的日志级别策略**:
- 开发环境:DEBUG级别
- 测试环境:INFO级别
- 生产环境:WARNING或ERROR级别
3. **日志文件管理**:
- 使用RotatingFileHandler防止日志文件过大
- 按日志级别分离文件(如info.log, error.log)
- 定期清理过期日志文件
4. **性能优化**:
```python
# 避免在DEBUG级别禁用时进行字符串格式化
if logger.isEnabledFor(logging.DEBUG):
logger.debug(f"复杂对象: {expensive_function()}")
# 使用参数化日志记录
logger.info("用户 %s 登录成功,IP: %s", username, ip_address)
```
5. **异常处理**:
```python
try:
risky_operation()
except Exception as e:
logger.error("操作失败: %s", str(e), exc_info=True) # 记录完整堆栈
logger.exception("操作失败") # 另一种记录异常的方式
```
通过以上配置和最佳实践,可以构建一个健壮、灵活且易于维护的日志系统,有效支持项目开发、测试和生产环境的日志需求。在实际项目中,还可以结合日志分析工具(如ELK Stack)对日志数据进行进一步处理和分析[ref_1]。