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
相关产品推荐
相关产品推荐

