You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django REST Framework日志配置异常:按天切分+100MB切割未生效

Django REST Framework 日志配置:实现按天+大小自动轮转

问题概述

需要为DRF应用配置日志,要求:

  • 按天生成日志文件
  • 当日志文件大小超过100MB时自动创建新文件
    当前配置未达到预期效果,日志未按规则生成。

原代码问题分析

  1. TimedRotatingFileHandler 局限性:仅支持按时间轮转,无法响应文件大小触发的轮转需求。
  2. 手动检查逻辑未生效:自定义的check_log_file_size函数未被调用,且逻辑存在缺陷——重命名文件后,日志handler仍指向原路径,后续日志会写入已重命名的文件,甚至引发IO错误。
  3. logger 实例引用问题:若视图中未正确导入logger_config.py中的logger实例,会导致日志无法写入目标文件。
  4. 路径处理疏漏: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 14:16:23