Django REST Framework日志配置异常:按天切分+100MB切割未生效
Django REST Framework 日志配置:实现按天+大小自动轮转
问题概述
需要为DRF应用配置日志,要求:
- 按天生成日志文件
- 当日志文件大小超过100MB时自动创建新文件
当前配置未达到预期效果,日志未按规则生成。
原代码问题分析
- TimedRotatingFileHandler 局限性:仅支持按时间轮转,无法响应文件大小触发的轮转需求。
- 手动检查逻辑未生效:自定义的
check_log_file_size函数未被调用,且逻辑存在缺陷——重命名文件后,日志handler仍指向原路径,后续日志会写入已重命名的文件,甚至引发IO错误。 - logger 实例引用问题:若视图中未正确导入
logger_config.py中的logger实例,会导致日志无法写入目标文件。 - 路径处理疏漏:
filepath初始值为None,调用检查函数时易引发参数错误;未确保日志目录存在,可能导致文件创建失败。
解决方案
方案一:使用第三方库(推荐)
借助concurrent-log-handler库的ConcurrentRotatingFileHandler,可同时支持按大小和时间轮转,且解决多进程写入冲突问题。
步骤1:安装依赖
pip install concurrent-log-handler
步骤2:修改logger_config.py
import logging import os from concurrent_log_handler import ConcurrentRotatingFileHandler import datetime # 确保日志目录存在 log_dir = "loggerFiles" os.makedirs(log_dir, exist_ok=True) log_file_path = os.path.join(log_dir, "app.log") # 初始化logger logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # 避免重复添加handler(防止多模块导入时日志重复输出) if not logger.handlers: # 配置轮转规则:100MB/文件,保留30个备份,同时按日期命名备份 handler = ConcurrentRotatingFileHandler( log_file_path, maxBytes=100 * 1024 * 1024, # 100MB backupCount=30, encoding="utf-8" ) # 自定义备份文件命名规则:添加日期后缀 def log_namer(name): base_name, ext = os.path.splitext(name) timestamp = datetime.datetime.now().strftime("%Y%m%d") return f"{base_name}_{timestamp}{ext}" handler.namer = log_namer # 设置日志格式 formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) handler.setFormatter(formatter) logger.addHandler(handler) # 测试日志输出 logger.debug("Debug message") logger.info("Info message") logger.warning("Warning message") logger.error("Error message")
步骤3:视图中正确使用
from .logger_config import logger from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status class MyView(APIView): def get(self, request): try: 1 / 0 # 模拟异常 except Exception: # exception方法自动记录异常栈信息,无需手动传入异常对象 logger.exception("请求处理发生异常") return Response( {"error": "An exception occurred"}, status=status.HTTP_400_BAD_REQUEST )
方案二:自定义Handler(不依赖第三方库)
继承TimedRotatingFileHandler,重写emit方法,添加文件大小检查逻辑,实现双条件轮转。
修改logger_config.py
import logging import os from logging.handlers import TimedRotatingFileHandler class SizeAndTimeRotatingHandler(TimedRotatingFileHandler): def __init__(self, filename, when="D", interval=1, backupCount=0, encoding=None, delay=False, utc=False, maxBytes=0): super().__init__(filename, when, interval, backupCount, encoding, delay, utc) self.maxBytes = maxBytes # 设置单文件最大大小 def emit(self, record): # 先检查文件大小,超过阈值则触发轮转 if self.maxBytes > 0: try: if os.path.exists(self.baseFilename): if os.path.getsize(self.baseFilename) >= self.maxBytes: self.doRollover() except Exception: pass # 调用父类方法写入日志 super().emit(record) # 初始化日志目录和logger log_dir = "loggerFiles" os.makedirs(log_dir, exist_ok=True) log_file_path = os.path.join(log_dir, "app.log") logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) if not logger.handlers: handler = SizeAndTimeRotatingHandler( log_file_path, when="D", interval=1, backupCount=30, maxBytes=100 * 1024 * 1024 # 100MB ) formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) handler.setFormatter(formatter) logger.addHandler(handler) # 测试日志 logger.debug("Debug message") logger.info("Info message") logger.warning("Warning message") logger.error("Error message")
关键注意事项
- 日志目录创建:必须确保日志存储目录存在,否则会触发文件创建失败异常。
- 避免重复Handler:添加
if not logger.handlers判断,防止多模块导入时重复添加Handler,导致日志重复输出。 - Logger实例一致性:视图中必须导入
logger_config.py中定义的logger实例,避免使用logging.getLogger(__name__)(不同模块的__name__不同,会生成独立的Logger)。 - 异常日志记录:使用
logger.exception()自动捕获并记录异常栈信息,方便排查问题。
内容的提问来源于stack exchange,提问作者Zain Jack
相关产品推荐
相关产品推荐

