Python日志记录:10个日志管理的最佳实践

文摘   2024-11-24 10:12   江苏  

日志记录是软件开发中的一个重要组成部分,它可以帮助开发者调试程序、追踪错误、监控系统状态等。Python 提供了强大的日志记录功能,通过 logging 模块可以轻松实现各种日志管理需求。本文将介绍 10 个 Python 日志管理的最佳实践,并通过实际代码示例帮助你更好地理解和应用这些技巧。

1. 使用 logging 模块的基本配置

首先,我们需要了解如何使用 logging 模块的基本配置。logging 模块提供了多种级别的日志记录,包括 DEBUGINFOWARNINGERRORCRITICAL

import logging

# 配置日志记录
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

# 记录不同级别的日志
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

输出结果:

2023-10-01 12:00:00,000 - DEBUG - This is a debug message
2023-10-01 12:00:00,001 - INFO - This is an info message
2023-10-01 12:00:00,002 - WARNING - This is a warning message
2023-10-01 12:00:00,003 - ERROR - This is an error message
2023-10-01 12:00:00,004 - CRITICAL - This is a critical message

2. 将日志记录到文件

除了将日志输出到控制台,我们还可以将日志记录到文件中,以便长期保存和分析。

import logging

# 配置日志记录到文件
logging.basicConfig(filename='app.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

# 记录日志
logging.debug('This is a debug message')
logging.info('This is an info message')

输出结果:

# app.log 文件内容
2023-10-01 12:00:00,000 - DEBUG - This is a debug message
2023-10-01 12:00:00,001 - INFO - This is an info message

3. 使用多个日志处理器

有时候我们需要同时将日志输出到控制台和文件中,这时可以使用多个日志处理器(Handler)。

import logging

# 创建日志记录器
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# 创建文件处理器
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_formatter = logging.Formatter('%(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)

# 添加处理器到日志记录器
logger.addHandler(file_handler)
logger.addHandler(console_handler)

# 记录日志
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.error('This is an error message')

输出结果:

# 控制台输出
INFO - This is an info message
ERROR - This is an error message

# app.log 文件内容
2023-10-01 12:00:00,000 - DEBUG - This is a debug message
2023-10-01 12:00:00,001 - INFO - This is an info message
2023-10-01 12:00:00,002 - ERROR - This is an error message

4. 使用日志过滤器

日志过滤器可以用来过滤特定的日志消息,例如只记录特定模块的日志。

import logging

# 创建日志记录器
logger = logging.getLogger('my_module')
logger.setLevel(logging.DEBUG)

# 创建文件处理器
file_handler = logging.FileHandler('my_module.log')
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 创建过滤器
class ModuleFilter(logging.Filter):
    def filter(self, record):
        return 'my_module' in record.name

file_handler.addFilter(ModuleFilter())

# 添加处理器到日志记录器
logger.addHandler(file_handler)

# 记录日志
logger.debug('This is a debug message from my_module')
logging.getLogger().info('This is an info message from root logger')

输出结果:

# my_module.log 文件内容
2023-10-01 12:00:00,000 - my_module - DEBUG - This is a debug message from my_module

5. 使用日志记录器的层级结构

logging 模块支持日志记录器的层级结构,可以通过父级记录器的配置影响子级记录器。

import logging

# 创建父级日志记录器
parent_logger = logging.getLogger('parent')
parent_logger.setLevel(logging.DEBUG)

# 创建文件处理器
file_handler = logging.FileHandler('parent.log')
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 添加处理器到父级日志记录器
parent_logger.addHandler(file_handler)

# 创建子级日志记录器
child_logger = logging.getLogger('parent.child')
child_logger.setLevel(logging.INFO)

# 记录日志
child_logger.debug('This is a debug message from child')
child_logger.info('This is an info message from child')

输出结果:

# parent.log 文件内容
2023-10-01 12:00:00,000 - parent.child - INFO - This is an info message from child

6. 使用日志记录器的命名空间

通过命名空间可以更好地组织和管理日志记录器,避免命名冲突。

import logging

# 创建命名空间日志记录器
logger = logging.getLogger('my_app.module1')
logger.setLevel(logging.DEBUG)

# 创建文件处理器
file_handler = logging.FileHandler('module1.log')
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 添加处理器到日志记录器
logger.addHandler(file_handler)

# 记录日志
logger.debug('This is a debug message from module1')

输出结果:

# module1.log 文件内容
2023-10-01 12:00:00,000 - my_app.module1 - DEBUG - This is a debug message from module1

7. 使用日志记录器的上下文信息

通过 extra 参数可以在日志记录时添加额外的上下文信息。

import logging

# 创建日志记录器
logger = logging.getLogger('context_logger')
logger.setLevel(logging.DEBUG)

# 创建文件处理器
file_handler = logging.FileHandler('context.log')
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(user)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 添加处理器到日志记录器
logger.addHandler(file_handler)

# 记录日志
logger.info('This is an info message', extra={'user''Alice'})

输出结果:

# context.log 文件内容
2023-10-01 12:00:00,000 - context_logger - INFO - Alice - This is an info message

8. 使用日志记录器的异步处理

对于高并发的应用,可以使用异步处理来提高日志记录的性能。

import logging
import queue
import threading

# 创建队列
log_queue = queue.Queue()

# 创建日志记录器
logger = logging.getLogger('async_logger')
logger.setLevel(logging.DEBUG)

# 创建文件处理器
file_handler = logging.FileHandler('async.log')
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 定义异步处理函数
def process_logs():
    while True:
        record = log_queue.get()
        if record is None:
            break
        file_handler.emit(record)

# 启动异步处理线程
thread = threading.Thread(target=process_logs)
thread.start()

# 自定义日志处理器
class QueueHandler(logging.Handler):
    def emit(self, record):
        log_queue.put_nowait(record)

# 添加自定义处理器到日志记录器
queue_handler = QueueHandler()
logger.addHandler(queue_handler)

# 记录日志
logger.debug('This is a debug message')
logger.info('This is an info message')

# 停止异步处理线程
log_queue.put(None)
thread.join()

输出结果:

# async.log 文件内容
2023-10-01 12:00:00,000 - async_logger - DEBUG - This is a debug message
2023-10-01 12:00:00,001 - async_logger - INFO - This is an info message

9. 使用日志记录器的轮转日志

轮转日志可以自动管理日志文件的大小和数量,避免日志文件过大或过多。

import logging
from logging.handlers import RotatingFileHandler

# 创建日志记录器
logger = logging.getLogger('rotating_logger')
logger.setLevel(logging.DEBUG)

# 创建轮转文件处理器
file_handler = RotatingFileHandler('rotating.log', maxBytes=1024, backupCount=5)
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 添加处理器到日志记录器
logger.addHandler(file_handler)

# 记录日志
for i in range(100):
    logger.debug(f'This is a debug message {i}')

输出结果:

# rotating.log 文件内容
2023-10-01 12:00:00,000 - rotating_logger - DEBUG - This is a debug message 0
2023-10-01 12:00:00,001 - rotating_logger - DEBUG - This is a debug message 1
...
2023-10-01 12:00:00,099 - rotating_logger - DEBUG - This is a debug message 99

10. 使用日志记录器的定时任务

定时任务可以定期清理或归档日志文件,保持系统的整洁。

import logging
import time
from logging.handlers import TimedRotatingFileHandler

# 创建日志记录器
logger = logging.getLogger('timed_logger')
logger.setLevel(logging.DEBUG)

# 创建定时轮转文件处理器
file_handler = TimedRotatingFileHandler('timed.log', when='S', interval=10, backupCount=5)
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 添加处理器到日志记录器
logger.addHandler(file_handler)

# 记录日志
for i in range(60):
    logger.debug(f'This is a debug message {i}')
    time.sleep(1)

输出结果:

# timed.log 文件内容
2023-10-01 12:00:00,000 - timed_logger - DEBUG - This is a debug message 0
2023-10-01 12:00:01,000 - timed_logger - DEBUG - This is a debug message 1
...
2023-10-01 12:01:00,000 - timed_logger - DEBUG - This is a debug message 59

实战案例:日志记录在 Web 应用中的应用

假设我们有一个简单的 Flask Web 应用,需要记录用户的访问日志和错误日志。我们将使用 logging 模块来实现这一需求。

from flask import Flask, request, jsonify
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

# 配置日志记录
access_logger = logging.getLogger('access_logger')
access_logger.setLevel(logging.INFO)

error_logger = logging.getLogger('error_logger')
error_logger.setLevel(logging.ERROR)

# 创建轮转文件处理器
access_file_handler = RotatingFileHandler('access.log', maxBytes=1024*1024, backupCount=5)
access_file_handler.setLevel(logging.INFO)
access_formatter = logging.Formatter('%(asctime)s - %(remote_addr)s - %(request_method)s - %(path)s - %(status_code)s')
access_file_handler.setFormatter(access_formatter)

error_file_handler = RotatingFileHandler('error.log', maxBytes=1024*1024, backupCount=5)
error_file_handler.setLevel(logging.ERROR)
error_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
error_file_handler.setFormatter(error_formatter)

# 添加处理器到日志记录器
access_logger.addHandler(access_file_handler)
error_logger.addHandler(error_file_handler)

@app.before_request
def log_access():
    remote_addr = request.remote_addr
    request_method = request.method
    path = request.path
    access_logger.info('', extra={'remote_addr': remote_addr, 'request_method': request_method, 'path': path, 'status_code'200})

@app.errorhandler(Exception)
def handle_error(e):
    error_logger.error(str(e))
    return jsonify({'error': str(e)}), 500

@app.route('/')
def index():
    return 'Hello, World!'

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

输出结果:

# access.log 文件内容
2023-10-01 12:00:00,000 - 127.0.0.1 - GET - / - 200

# error.log 文件内容
2023-10-01 12:00:00,000 - ERROR - Some unexpected error occurred

总结

本文介绍了 10 个 Python 日志管理的最佳实践,包括基本配置、日志记录到文件、使用多个日志处理器、日志过滤器、日志记录器的层级结构、命名空间、上下文信息、异步处理、轮转日志和定时任务。通过这些技巧,你可以更好地管理和优化你的日志记录系统。最后,我们还通过一个实战案例展示了如何在 Flask Web 应用中应用这些技巧。

好了,今天的分享就到这里了,我们下期见。如果本文对你有帮助,请动动你可爱的小手指点赞、转发、在看吧!

付费合集推荐

Python编程基础

Python办公自动化-Excel

微信公众号批量上传发布系统

文末福利

公众号消息窗口回复“编程资料”,获取Python编程、人工智能、爬虫等100+本精品电子书。

精品系统

微信公众号批量上传发布系统

关注我👇,精彩不再错过


手把手PythonAI编程
分享与人工智能和python编程语言相关的笔记和项目经历。
 最新文章