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

Django runserver日志滚动时出现文件访问权限错误排查

解决Django runserver下RotatingFileHandler的文件权限错误

问题根源

Django的runserver在开发模式下默认会启动两个进程:一个主进程负责监控文件变化自动重载,另一个是实际处理请求的工作进程。你的apps.py中ready()方法会在每个进程启动时执行一次,导致custom_logging.init()被调用两次——两个进程都创建了RotatingFileHandler并持有同一个日志文件的句柄。当触发日志滚动时,其中一个进程尝试重命名文件,另一个进程仍占用文件锁,最终抛出PermissionError。

解决方案

方案1:仅在主进程初始化日志

利用环境变量标记主进程,避免子进程重复执行初始化逻辑:

# apps.py
import sys
import os
from django.apps import AppConfig

class YourAppConfig(AppConfig):
    name = 'your_app'

    def ready(self):
        is_manage_py = any(arg.casefold().endswith("manage.py") for arg in sys.argv)
        is_runserver = any(arg.casefold() == "runserver" for arg in sys.argv)
        is_shell     = any(arg.casefold() == "shell" for arg in sys.argv)

        if is_manage_py and (is_runserver or is_shell):
            # 通过环境变量标记是否已初始化,避免子进程重复执行
            if not os.environ.get('DJANGO_LOG_INITIALIZED'):
                os.environ['DJANGO_LOG_INITIALIZED'] = '1'
                import custom_logging
                custom_logging.init()

方案2:改用Django原生LOGGING配置

将日志配置移到settings.py的LOGGING字典中,Django会自动处理进程间的日志初始化,避免重复绑定处理器:

# settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
        },
    },
    'handlers': {
        'rotating_file': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'logs/main.log',
            'maxBytes': 1024*1024*5,  # 单文件最大5MB
            'backupCount': 5,  # 保留5个备份文件
            'formatter': 'standard',
        },
    },
    'loggers': {
        'your_app': {
            'handlers': ['rotating_file'],
            'level': 'INFO',
            'propagate': True,
        },
    }
}

这种方式无需在apps.py中手动调用初始化,Django启动时会自动加载配置,从根源避免重复绑定处理器的问题。

方案3:检查日志器是否已有目标处理器

在set_logger方法中先判断是否已添加过RotatingFileHandler,避免重复创建:

# custom_logging.py
import logging
from logging.handlers import RotatingFileHandler

def set_logger(logger):
    # 遍历已有处理器,若存在RotatingFileHandler则跳过
    for handler in logger.handlers:
        if isinstance(handler, RotatingFileHandler):
            return
    # 未找到则创建并添加处理器
    formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')
    file_handler = RotatingFileHandler(
        'logs/main.log', 
        maxBytes=5*1024*1024, 
        backupCount=5
    )
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)

def init():
    target_logger = logging.getLogger('your_app')
    set_logger(target_logger)

验证

修改后启动runserver,手动写入大量日志触发滚动,确认不再抛出PermissionError即可。

内容的提问来源于stack exchange,提问作者Vegard

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:03:15