Python日志记录:logging模块的6个配置选项
大家好!今天我们来聊聊 Python 中非常重要的一个模块——logging
。这个模块帮助我们记录程序运行过程中的各种信息,对于调试和维护程序来说非常重要。
什么是 logging?
logging
是 Python 的标准库之一,它允许开发者记录程序运行时的状态信息。这些信息可以帮助我们更好地理解程序的行为,尤其是在处理错误或者进行性能优化的时候。
为什么使用 logging?
调试:记录程序运行时的状态,方便调试。
审计:跟踪程序执行的过程,便于审计。
日志分析:收集运行时数据,便于后续分析。
错误追踪:记录错误信息,方便追踪问题。
安装和导入
首先,确保你的 Python 环境中已经安装了 logging
模块。这是 Python 标准库的一部分,所以通常不需要单独安装。
import logging
接下来,让我们一起学习 logging
模块的六个主要配置选项。
1. 设置日志级别
日志级别用于定义记录信息的重要性。常见的级别有:
DEBUG
:详细的信息,通常只在调试时开启。INFO
:确认一切按预期运行。WARNING
:异常情况发生,但不影响程序运行。ERROR
:由于更严重的问题,某些功能无法执行。CRITICAL
:严重错误,导致程序崩溃或无法继续执行。
你可以设置全局的日志级别,也可以为不同的日志记录器设置不同的级别。
示例:设置日志级别
import logging
# 设置日志级别为 INFO
logging.basicConfig(level=logging.INFO)
logging.debug('这是一条 debug 信息') # 不会被打印
logging.info('这是一条 info 信息') # 会被打印
logging.warning('这是一条 warning 信息') # 会被打印
logging.error('这是一条 error 信息') # 会被打印
logging.critical('这是一条 critical 信息') # 会被打印
输出:
INFO:root:这是一条 info 信息
WARNING:root:这是一条 warning 信息
ERROR:root:这是一条 error 信息
CRITICAL:root:这是一条 critical 信息
2. 创建日志记录器
为了更好地组织日志,我们可以创建多个日志记录器(logger)。每个记录器可以有自己的配置,比如日志级别和格式。
示例:创建日志记录器
import logging
# 创建一个名为 'app' 的记录器
logger = logging.getLogger('app')
# 设置日志级别为 DEBUG
logger.setLevel(logging.DEBUG)
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(console_handler)
# 记录日志
logger.debug('这是一条 debug 信息')
logger.info('这是一条 info 信息')
logger.warning('这是一条 warning 信息')
logger.error('这是一条 error 信息')
logger.critical('这是一条 critical 信息')
输出:
2023-10-03 14:50:23,123 - app - DEBUG - 这是一条 debug 信息
2023-10-03 14:50:23,123 - app - INFO - 这是一条 info 信息
2023-10-03 14:50:23,123 - app - WARNING - 这是一条 warning 信息
2023-10-03 14:50:23,123 - app - ERROR - 这是一条 error 信息
2023-10-03 14:50:23,123 - app - CRITICAL - 这是一条 critical 信息
3. 配置日志格式
日志格式定义了日志消息的外观。你可以自定义日期格式、日志级别、消息内容等。
示例:配置日志格式
import logging
# 创建一个名为 'app' 的记录器
logger = logging.getLogger('app')
# 设置日志级别为 DEBUG
logger.setLevel(logging.DEBUG)
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - [%(levelname)s] - %(message)s')
console_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(console_handler)
# 记录日志
logger.debug('这是一条 debug 信息')
logger.info('这是一条 info 信息')
logger.warning('这是一条 warning 信息')
logger.error('这是一条 error 信息')
logger.critical('这是一条 critical 信息')
输出:
2023-10-03 14:50:23 - [DEBUG] - 这是一条 debug 信息
2023-10-03 14:50:23 - [INFO] - 这是一条 info 信息
2023-10-03 14:50:23 - [WARNING] - 这是一条 warning 信息
2023-10-03 14:50:23 - [ERROR] - 这是一条 error 信息
2023-10-03 14:50:23 - [CRITICAL] - 这是一条 critical 信息
4. 使用文件处理器
除了将日志输出到控制台,你还可以将日志保存到文件中,以便后续查看和分析。
示例:使用文件处理器
import logging
# 创建一个名为 'app' 的记录器
logger = logging.getLogger('app')
# 设置日志级别为 DEBUG
logger.setLevel(logging.DEBUG)
# 创建文件处理器
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - [%(levelname)s] - %(message)s')
file_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(file_handler)
# 记录日志
logger.debug('这是一条 debug 信息')
logger.info('这是一条 info 信息')
logger.warning('这是一条 warning 信息')
logger.error('这是一条 error 信息')
logger.critical('这是一条 critical 信息')
输出:app.log
文件内容如下:
2023-10-03 14:50:23 - [DEBUG] - 这是一条 debug 信息
2023-10-03 14:50:23 - [INFO] - 这是一条 info 信息
2023-10-03 14:50:23 - [WARNING] - 这是一条 warning 信息
2023-10-03 14:50:23 - [ERROR] - 这是一条 error 信息
2023-10-03 14:50:23 - [CRITICAL] - 这是一条 critical 信息
5. 配置日志文件轮转
当日志文件变得非常大时,你可以使用日志文件轮转功能。这样可以在达到一定条件时生成新的日志文件,旧的日志文件会被备份。
示例:配置日志文件轮转
import logging
from logging.handlers import RotatingFileHandler
# 创建一个名为 'app' 的记录器
logger = logging.getLogger('app')
# 设置日志级别为 DEBUG
logger.setLevel(logging.DEBUG)
# 创建文件处理器
file_handler = RotatingFileHandler('app.log', maxBytes=1024 * 1024, backupCount=5)
file_handler.setLevel(logging.DEBUG)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - [%(levelname)s] - %(message)s')
file_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(file_handler)
# 记录大量日志
for i in range(10000):
logger.debug(f'这是第 {i} 条 debug 信息')
logger.info(f'这是第 {i} 条 info 信息')
logger.warning(f'这是第 {i} 条 warning 信息')
logger.error(f'这是第 {i} 条 error 信息')
logger.critical(f'这是第 {i} 条 critical 信息')
在这个示例中,当 app.log
文件大小超过 1MB 时,会生成新的日志文件,并保留最多 5 份旧的日志文件。
6. 配置日志文件按时间分割
另一种常见的日志管理方式是按时间分割日志文件。这样可以更方便地管理和查找特定时间段的日志。
示例:配置日志文件按时间分割
import logging
from logging.handlers import TimedRotatingFileHandler
# 创建一个名为 'app' 的记录器
logger = logging.getLogger('app')
# 设置日志级别为 DEBUG
logger.setLevel(logging.DEBUG)
# 创建文件处理器
file_handler = TimedRotatingFileHandler('app.log', when='midnight', interval=1, backupCount=7)
file_handler.setLevel(logging.DEBUG)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - [%(levelname)s] - %(message)s')
file_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(file_handler)
# 记录日志
logger.debug('这是一条 debug 信息')
logger.info('这是一条 info 信息')
logger.warning('这是一条 warning 信息')
logger.error('这是一条 error 信息')
logger.critical('这是一条 critical 信息')
实战案例分析
假设你正在开发一个 Web 应用程序,需要记录用户请求和系统状态。下面是一个更复杂的示例,展示了如何使用 logging
模块来记录这些信息,并进行更详细的配置。
示例:Web 应用程序日志记录
import logging
from logging.handlers import TimedRotatingFileHandler
# 创建一个名为 'web_app' 的记录器
logger = logging.getLogger('web_app')
# 设置日志级别为 INFO
logger.setLevel(logging.INFO)
# 创建文件处理器
file_handler = TimedRotatingFileHandler('web_app.log', when='midnight', interval=1, backupCount=7)
file_handler.setLevel(logging.INFO)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - [%(levelname)s] - [%(name)s] - %(message)s')
file_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(file_handler)
def handle_request(user_id, request_type, response_status):
logger.info(f'用户 {user_id} 发起了 {request_type} 请求,响应状态码为 {response_status}')
# 模拟处理请求
handle_request(1, 'GET', 200)
handle_request(2, 'POST', 201)
handle_request(3, 'PUT', 400)
handle_request(4, 'DELETE', 404)
输出:web_app.log
文件内容如下:
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 1 发起了 GET 请求,响应状态码为 200
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 2 发起了 POST 请求,响应状态码为 201
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 3 发起了 PUT 请求,响应状态码为 400
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 4 发起了 DELETE 请求,响应状态码为 404
在这个示例中,我们不仅记录了用户的请求类型,还记录了响应状态码。这样可以更全面地了解系统的运行情况。
多处理器配置
在实际应用中,你可能需要同时将日志输出到控制台和文件。这样可以在开发阶段实时查看日志,同时将完整的日志记录保存到文件中以备后续分析。
示例:多处理器配置
import logging
from logging.handlers import TimedRotatingFileHandler
# 创建一个名为 'web_app' 的记录器
logger = logging.getLogger('web_app')
# 设置日志级别为 INFO
logger.setLevel(logging.INFO)
# 创建文件处理器
file_handler = TimedRotatingFileHandler('web_app.log', when='midnight', interval=1, backupCount=7)
file_handler.setLevel(logging.INFO)
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - [%(levelname)s] - [%(name)s] - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
def handle_request(user_id, request_type, response_status):
logger.info(f'用户 {user_id} 发起了 {request_type} 请求,响应状态码为 {response_status}')
# 模拟处理请求
handle_request(1, 'GET', 200)
handle_request(2, 'POST', 201)
handle_request(3, 'PUT', 400)
handle_request(4, 'DELETE', 404)
输出:控制台输出如下:
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 1 发起了 GET 请求,响应状态码为 200
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 2 发起了 POST 请求,响应状态码为 201
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 3 发起了 PUT 请求,响应状态码为 400
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 4 发起了 DELETE 请求,响应状态码为 404
文件 web_app.log
输出如下:
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 1 发起了 GET 请求,响应状态码为 200
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 2 发起了 POST 请求,响应状态码为 201
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 3 发起了 PUT 请求,响应状态码为 400
2023-10-03 14:50:23 - [INFO] - [web_app] - 用户 4 发起了 DELETE 请求,响应状态码为 404
在这个示例中,我们同时使用了文件处理器和控制台处理器,确保日志既能实时显示在控制台上,又能完整保存在文件中。
自定义日志格式
除了基本的日志格式,你还可以根据需要自定义日志格式。例如,可以包含更多的信息,如用户的 IP 地址、请求 URL 等。
示例:自定义日志格式
import logging
from logging.handlers import TimedRotatingFileHandler
# 创建一个名为 'web_app' 的记录器
logger = logging.getLogger('web_app')
# 设置日志级别为 INFO
logger.setLevel(logging.INFO)
# 创建文件处理器
file_handler = TimedRotatingFileHandler('web_app.log', when='midnight', interval=1, backupCount=7)
file_handler.setLevel(logging.INFO)
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 创建自定义日志格式
formatter = logging.Formatter('%(asctime)s - [%(levelname)s] - [%(name)s] - [%(client_ip)s] - [%(request_url)s] - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
def handle_request(user_id, request_type, response_status, client_ip, request_url):
logger.info(f'用户 {user_id} 发起了 {request_type} 请求,客户端 IP 为 {client_ip},请求 URL 为 {request_url},响应状态码为 {response_status}')
# 模拟处理请求
handle_request(1, 'GET', 200, '192.168.1.1', '/api/v1/users')
handle_request(2, 'POST', 201, '192.168.1.2', '/api/v1/posts')
handle_request(3, 'PUT', 400, '192.168.1.3', '/api/v1/comments/1')
handle_request(4, 'DELETE', 404, '192.168.1.4', '/api/v1/comments/2')
输出:控制台输出如下:
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.1] - [/api/v1/users] - 用户 1 发起了 GET 请求,客户端 IP 为 192.168.1.1,请求 URL 为 /api/v1/users,响应状态码为 200
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.2] - [/api/v1/posts] - 用户 2 发起了 POST 请求,客户端 IP 为 192.168.1.2,请求 URL 为 /api/v1/posts,响应状态码为 201
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.3] - [/api/v1/comments/1] - 用户 3 发起了 PUT 请求,客户端 IP 为 192.168.1.3,请求 URL 为 /api/v1/comments/1,响应状态码为 400
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.4] - [/api/v1/comments/2] - 用户 4 发起了 DELETE 请求,客户端 IP 为 192.168.1.4,请求 URL 为 /api/v1/comments/2,响应状态码为 404
文件 web_app.log
输出如下:
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.1] - [/api/v1/users] - 用户 1 发起了 GET 请求,客户端 IP 为 192.168.1.1,请求 URL 为 /api/v1/users,响应状态码为 200
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.2] - [/api/v1/posts] - 用户 2 发起了 POST 请求,客户端 IP 为 192.168.1.2,请求 URL 为 /api/v1/posts,响应状态码为 201
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.3] - [/api/v1/comments/1] - 用户 3 发起了 PUT 请求,客户端 IP 为 192.168.1.3,请求 URL 为 /api/v1/comments/1,响应状态码为 400
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.4] - [/api/v1/comments/2] - 用户 4 发起了 DELETE 请求,客户端 IP 为 192.168.1.4,请求 URL 为 /api/v1/comments/2,响应状态码为 404
在这个示例中,我们增加了客户端 IP 和请求 URL 两个字段,使得日志信息更加丰富和有用。
高级技巧:使用上下文管理器
在一些情况下,你可能需要在特定的上下文中记录日志。例如,在某个函数内部记录日志时,可以使用上下文管理器来确保日志的正确性。
示例:使用上下文管理器
import logging
from contextlib import contextmanager
# 创建一个名为 'web_app' 的记录器
logger = logging.getLogger('web_app')
# 设置日志级别为 INFO
logger.setLevel(logging.INFO)
# 创建文件处理器
file_handler = TimedRotatingFileHandler('web_app.log', when='midnight', interval=1, backupCount=7)
file_handler.setLevel(logging.INFO)
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - [%(levelname)s] - [%(name)s] - [%(client_ip)s] - [%(request_url)s] - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
@contextmanager
def log_context(client_ip, request_url):
try:
yield
finally:
logger.info(f'客户端 IP 为 {client_ip},请求 URL 为 {request_url}')
def handle_request(user_id, request_type, response_status, client_ip, request_url):
with log_context(client_ip, request_url):
logger.info(f'用户 {user_id} 发起了 {request_type} 请求,响应状态码为 {response_status}')
# 模拟处理请求
handle_request(1, 'GET', 200, '192.168.1.1', '/api/v1/users')
handle_request(2, 'POST', 201, '192.168.1.2', '/api/v1/posts')
handle_request(3, 'PUT', 400, '192.168.1.3', '/api/v1/comments/1')
handle_request(4, 'DELETE', 404, '192.168.1.4', '/api/v1/comments/2')
输出:控制台输出如下:
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.1] - [/api/v1/users] - 用户 1 发起了 GET 请求,响应状态码为 200
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.1] - [/api/v1/users]
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.2] - [/api/v1/posts] - 用户 2 发起了 POST 请求,响应状态码为 201
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.2] - [/api/v1/posts]
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.3] - [/api/v1/comments/1] - 用户 3 发起了 PUT 请求,响应状态码为 400
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.3] - [/api/v1/comments/1]
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.4] - [/api/v1/comments/2] - 用户 4 发起了 DELETE 请求,响应状态码为 404
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.4] - [/api/v1/comments/2]
文件 web_app.log
输出如下:
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.1] - [/api/v1/users] - 用户 1 发起了 GET 请求,响应状态码为 200
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.1] - [/api/v1/users]
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.2] - [/api/v1/posts] - 用户 2 发起了 POST 请求,响应状态码为 201
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.2] - [/api/v1/posts]
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.3] - [/api/v1/comments/1] - 用户 3 发起了 PUT 请求,响应状态码为 400
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.3] - [/api/v1/comments/1]
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.4] - [/api/v1/comments/2] - 用户 4 发起了 DELETE 请求,响应状态码为 404
2023-10-03 14:50:23 - [INFO] - [web_app] - [192.168.1.4] - [/api/v1/comments/2]
在这个示例中,我们使用了上下文管理器来确保每次处理请求时都会记录客户端 IP 和请求 URL。这样可以确保日志的一致性和准确性。