日志记录是软件开发中的一个重要组成部分,它可以帮助开发者调试程序、追踪错误、监控系统状态等。Python 提供了强大的日志记录功能,通过 logging 模块可以轻松实现各种日志管理需求。本文将介绍 10 个 Python 日志管理的最佳实践,并通过实际代码示例帮助你更好地理解和应用这些技巧。
首先,我们需要了解如何使用 logging 模块的基本配置。logging 模块提供了多种级别的日志记录,包括 DEBUG、INFO、WARNING、ERROR 和 CRITICAL。
importlogging # 配置日志记录 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-0112:00:00,000-DEBUG-This is a debug message2023-10-0112:00:00,001-INFO-This is an info message2023-10-0112:00:00,002-WARNING-This is a warning message2023-10-0112:00:00,003-ERROR-This is an error message2023-10-0112:00:00,004-CRITICAL-This is a critical message
除了将日志输出到控制台,我们还可以将日志记录到文件中,以便长期保存和分析。
importlogging # 配置日志记录到文件 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-0112:00:00,000-DEBUG-This is a debug message2023-10-0112:00:00,001-INFO-This is an info message
有时候我们需要同时将日志输出到控制台和文件中,这时可以使用多个日志处理器(Handler)。
importlogging # 创建日志记录器 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 messageERROR-This is an error message # app.log 文件内容2023-10-0112:00:00,000-DEBUG-This is a debug message2023-10-0112:00:00,001-INFO-This is an info message2023-10-0112:00:00,002-ERROR-This is an error message
日志过滤器可以用来过滤特定的日志消息,例如只记录特定模块的日志。
importlogging # 创建日志记录器 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)# 创建过滤器classModuleFilter(logging.Filter):deffilter(self,record):return'my_module'inrecord.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-0112:00:00,000-my_module-DEBUG-This is a debug message from my_module
logging 模块支持日志记录器的层级结构,可以通过父级记录器的配置影响子级记录器。
importlogging # 创建父级日志记录器 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')
输出结果:
# parent.log 文件内容2023-10-0112:00:00,000-parent.child-INFO-This is an info message from child
通过命名空间可以更好地组织和管理日志记录器,避免命名冲突。
importlogging # 创建命名空间日志记录器 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-0112:00:00,000-my_app.module1-DEBUG-This is a debug message from module1
通过 extra 参数可以在日志记录时添加额外的上下文信息。
importlogging # 创建日志记录器 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-0112:00:00,000-context_logger-INFO-Alice-This is an info message
对于高并发的应用,可以使用异步处理来提高日志记录的性能。
importloggingimportqueueimportthreading # 创建队列 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)# 定义异步处理函数 defprocess_logs():whileTrue:record=log_queue.get()ifrecord is None:breakfile_handler.emit(record)# 启动异步处理线程 thread=threading.Thread(target=process_logs)thread.start()# 自定义日志处理器classQueueHandler(logging.Handler):defemit(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-0112:00:00,000-async_logger-DEBUG-This is a debug message2023-10-0112:00:00,001-async_logger-INFO-This is an info message
轮转日志可以自动管理日志文件的大小和数量,避免日志文件过大或过多。
importlogging from logging.handlersimportRotatingFileHandler # 创建日志记录器 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)# 记录日志foriinrange(100):logger.debug(f'This is a debug message {i}')
输出结果:
# rotating.log 文件内容2023-10-0112:00:00,000-rotating_logger-DEBUG-This is a debug message02023-10-0112:00:00,001-rotating_logger-DEBUG-This is a debug message1...2023-10-0112:00:00,099-rotating_logger-DEBUG-This is a debug message99
定时任务可以定期清理或归档日志文件,保持系统的整洁。
importloggingimporttime from logging.handlersimportTimedRotatingFileHandler # 创建日志记录器 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)# 记录日志foriinrange(60):logger.debug(f'This is a debug message {i}')time.sleep(1)
输出结果:
# timed.log 文件内容2023-10-0112:00:00,000-timed_logger-DEBUG-This is a debug message02023-10-0112:00:01,000-timed_logger-DEBUG-This is a debug message1...2023-10-0112:01:00,000-timed_logger-DEBUG-This is a debug message59
假设我们有一个简单的 Flask Web 应用,需要记录用户的访问日志和错误日志。我们将使用 logging 模块来实现这一需求。
from flaskimportFlask,request,jsonifyimportlogging from logging.handlersimportRotatingFileHandler 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 deflog_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)defhandle_error(e):error_logger.error(str(e))returnjsonify({'error':str(e)}),500@app.route('/')defindex():return'Hello, World!'if__name__=='__main__':app.run(debug=True)
输出结果:
# access.log 文件内容2023-10-0112:00:00,000-127.0.0.1-GET-/-200# error.log 文件内容2023-10-0112:00:00,000-ERROR-Some unexpected error occurred
本文介绍了 10 个 Python 日志管理的最佳实践,包括基本配置、日志记录到文件、使用多个日志处理器、日志过滤器、日志记录器的层级结构、命名空间、上下文信息、异步处理、轮转日志和定时任务。通过这些技巧,你可以更好地管理和优化你的日志记录系统。最后,我们还通过一个实战案例展示了如何在 Flask Web 应用中应用这些技巧。