跳到主要内容

Python 日志记录

作为一个开发者,你可能经常在代码中使用 print 语句来输出变量值或调试信息。然而,当你的应用程序越来越复杂,仅仅依赖 print 语句来追踪程序行为变得不够理想。这时,Python的日志记录系统就派上用场了。

为什么需要日志记录?

日志记录的优势
  • 灵活性:可以将日志保存到文件、发送到网络、通过电子邮件发送等
  • 分级:可以根据重要性区分不同的日志信息
  • 格式化:可以定制日志的输出格式
  • 可配置性:可以在运行时更改日志行为

Python 的logging模块

Python的标准库提供了功能强大的 logging 模块,它是进行日志记录的主要工具。

基本使用

让我们从一个简单的例子开始:

python
import logging

# 配置基本的日志格式
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 记录不同级别的日志
logging.debug("这是一条调试信息")
logging.info("这是一条普通信息")
logging.warning("这是一条警告")
logging.error("这是一条错误信息")
logging.critical("这是一条严重错误信息")

输出结果:

2023-10-25 15:30:45,123 - INFO - 这是一条普通信息
2023-10-25 15:30:45,125 - WARNING - 这是一条警告
2023-10-25 15:30:45,125 - ERROR - 这是一条错误信息
2023-10-25 15:30:45,126 - CRITICAL - 这是一条严重错误信息
注意

上面的例子中,debug级别的日志没有显示出来,因为我们将日志级别设置为INFO。只有INFO及以上级别的日志才会被记录。

日志级别

Python的logging模块定义了以下几个级别,从低到高依次为:

级别数值使用场景
DEBUG10详细信息,通常用于诊断问题
INFO20确认程序按预期运行
WARNING30表示可能出现的问题
ERROR40由于更严重的问题,程序未能执行某些功能
CRITICAL50严重错误,表明程序可能无法继续运行

将日志保存到文件

将日志记录到文件中是一种常见需求,可以通过 filename 参数实现:

python
import logging

# 配置日志记录到文件
logging.basicConfig(
filename='app.log',
filemode='w', # 'w'表示覆盖模式,'a'表示追加模式
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

logging.debug("这条信息会被记录到文件中")

高级配置

当你需要更复杂的日志配置时,你可以使用日志处理器(handlers)和格式化器(formatters)。

处理器(Handlers)

处理器决定如何处理日志信息:可以将其写入文件、发送到控制台或通过网络发送。

以下是同时将日志发送到控制台和文件的例子:

python
import logging

# 创建一个logger
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# 创建文件处理器
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)

# 创建格式器
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

# 添加处理器到logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)

# 记录日志
logger.debug('这是一条调试信息')
logger.info('这是一条普通信息')
logger.warning('这是一条警告')

格式化器(Formatters)

格式化器定义了日志信息的格式。以下是一些常用的格式说明符:

格式说明符描述
%(asctime)s日志创建时间
%(name)s记录器的名称
%(levelname)s日志级别
%(message)s日志信息
%(filename)s文件名
%(lineno)d行号
%(funcName)s函数名
%(thread)d线程ID
%(threadName)s线程名称
%(process)d进程ID

实际应用场景

场景1:Web应用日志记录

在Flask或Django等Web应用中,日志记录对于追踪用户请求和错误非常重要:

python
import logging
from flask import Flask, request

# 配置日志
logging.basicConfig(
filename='web_app.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)

app = Flask(__name__)

@app.route('/')
def home():
# 记录用户访问
logging.info(f"用户访问主页 - IP: {request.remote_addr}")
return "Welcome to the home page!"

@app.route('/api/data')
def get_data():
try:
# 模拟可能出错的操作
result = 1 / 0
except Exception as e:
# 记录错误
logging.error(f"数据请求出错: {str(e)}")
return "Error processing request", 500

return {"data": "some data"}

if __name__ == '__main__':
app.run(debug=True)

场景2:数据处理脚本

对于长时间运行的数据处理脚本,日志可以帮助追踪进度和潜在问题:

python
import logging
import time

# 配置日志
logging.basicConfig(
filename='data_processing.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)

def process_data(data):
logging.info(f"开始处理数据集,包含 {len(data)} 条记录")

for i, item in enumerate(data):
try:
# 处理数据的代码
time.sleep(0.1) # 模拟处理时间

# 每处理100条记录,记录一次进度
if (i + 1) % 100 == 0:
logging.info(f"已处理 {i + 1}/{len(data)} 条记录")

except Exception as e:
logging.error(f"处理记录 {i} 时出错: {str(e)}")

logging.info("数据处理完成")

# 模拟数据
sample_data = [i for i in range(500)]
process_data(sample_data)

日志记录的最佳实践

  1. 为每个模块创建单独的日志记录器

    python
    logger = logging.getLogger(__name__)
  2. 设置合适的日志级别

    • 开发环境使用DEBUG
    • 生产环境使用INFO或WARNING
  3. 使用结构化日志: 包含足够的上下文信息,如用户ID、请求ID等

  4. 分离配置和使用: 在应用启动时配置日志,在代码中只使用日志

  5. 定期轮换日志文件: 使用 RotatingFileHandlerTimedRotatingFileHandler

    python
    handler = logging.handlers.TimedRotatingFileHandler(
    'app.log',
    when='midnight', # 每天午夜轮换
    backupCount=7 # 保留最近7天的日志
    )

配置文件方式

对于复杂的应用,可以通过配置文件方式配置日志:

python
import logging
import logging.config
import yaml # 需要安装pyyaml包

# 从配置文件加载配置
with open('logging_config.yaml', 'r') as f:
config = yaml.safe_load(f.read())
logging.config.dictConfig(config)

# 获取logger
logger = logging.getLogger(__name__)

# 使用logger
logger.debug("Debug message")
logger.info("Info message")

logging_config.yaml示例:

yaml
version: 1
formatters:
simple:
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
level: INFO
formatter: simple
stream: ext://sys.stdout
file:
class: logging.FileHandler
level: DEBUG
formatter: simple
filename: app.log
loggers:
__main__:
level: DEBUG
handlers: [console, file]
propagate: no
root:
level: INFO
handlers: [console]

总结

Python的日志记录功能提供了一个强大而灵活的方式来记录应用程序的行为。相比简单的print语句,它具有以下优势:

  • 可以根据严重程度分级
  • 可以配置输出到多个目标(文件、控制台等)
  • 格式可定制
  • 可以在不修改代码的情况下更改日志行为

通过合理使用日志记录,你可以更容易地监控应用程序的运行状态、定位问题,以及了解用户的使用模式。

练习

  1. 创建一个简单的脚本,使用不同级别记录日志到控制台
  2. 修改脚本,将DEBUG级别及以上的日志记录到文件,将WARNING级别及以上的日志输出到控制台
  3. 创建一个模拟的Web应用程序,记录用户访问和错误情况
  4. 使用配置文件方式配置日志系统

附加资源

通过掌握Python的日志记录功能,你将能够开发更健壮、更易于维护的应用程序。日志记录是每个专业Python开发者都应该熟练掌握的技能。