Django (DRF) 上有一项服务,其中配置了日志记录子系统。该服务还有一个执行某些任务的 Celery worker。我想以这样一种方式设置日志记录,即所有日志(Django 和 Celery)都保存在 .log 文件中,同时使用 RotatingFileHandler “滚动”文件,以免阻塞磁盘空间。我必须马上说我是登录 Django 的新手。
settings.py 文件中的 LOGGING 设置:
# Настройки логирования.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
'format': '[{levelname}]-[{asctime}]-[{module}]-[{process:d}]-[{thread:d}]: {message}',
'style': '{',
},
},
'handlers': {
'applications_logfile': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'default',
'filename': LOGS_DIR / 'applications/applications.log',
'maxBytes': 300,
'backupCount': 5,
},
'celery_worker_logfile': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'default',
'filename': LOGS_DIR / 'celery/worker.log',
'maxBytes': 300,
'backupCount': 5,
},
},
'loggers': {
'celery': {
'level': 'INFO',
'handlers': ['celery_worker_logfile'],
},
'api.application_handler.views': {
'level': 'INFO',
'handlers': ['applications_logfile'],
},
},
}
在 celery.py 文件中设置日志记录:
# Настройка логирования для Celery. Используем настройки логирования проекта.
@setup_logging.connect
def config_loggers(*args, **kwargs) -> None:
from logging.config import dictConfig
from django.conf import settings
dictConfig(settings.LOGGING)
我还禁用了 Celery 的根记录器捕获:
# Настройки Celery.
CELERY_BROKER_URL = config('CELERY_BROKER_URL')
CELERYD_HIJACK_ROOT_LOGGER = False
问题是,当任何可滚动的 .log 文件达到其最大大小并且 python 尝试滚动文件时,它会失败并出现以下错误:
PermissionError: [WinError 32] Процесс не может получить доступ к файлу, так как этот файл занят другим процессом
. 工人中有什么,django中有什么。
原因。在对这种行为进行了长时间的研究之后,我意识到 django 进程保存了一个 .log 文件,celery worker 写入,而 celery worker 保存了 django 进程写入的 .log 文件。.log 文件的“滚动”(旋转)是通过创建一个写入日志的空 .log 文件并重命名旧的 .log 文件来完成的。这就是问题所在。没有进程可以重命名该文件,因为它正被另一个进程使用。
我试图做的事情。我试图将 celery 和 django 日志记录设置完全分开,从 settings.py 中删除 celery 的所有设置并将它们写入 celery.py 中,以便两个进程根本不会接触彼此的文件。
设置.py:
# Настройки логирования.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
'format': '[{levelname}]-[{asctime}]-[{module}]-[{process:d}]-[{thread:d}]: {message}',
'style': '{',
},
},
'handlers': {
'applications_logfile': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'default',
'filename': LOGS_DIR / 'applications/applications.log',
'maxBytes': 300,
'backupCount': 5,
},
},
'loggers': {
'api.application_handler.views': {
'level': 'INFO',
'handlers': ['applications_logfile'],
},
},
}
芹菜.py
# Настройка логирования для Celery.
@setup_logging.connect
def config_loggers(*args, **kwargs) -> None:
logger = logging.getLogger('celery')
logger.setLevel(logging.INFO)
formatter = logging.Formatter(
fmt='[{levelname}]-[{asctime}]-[{module}]-[{process:d}]-[{thread:d}]: {message}',
style='{',
)
handler = handlers.RotatingFileHandler(
filename=settings.LOGS_DIR / 'celery/worker.log',
maxBytes=300,
backupCount=5,
)
handler.setFormatter(formatter)
logger.addHandler(handler)
之后django进程就不再保留worker写入的.log文件了,轮转成功了!但由于某种原因,celery-worker 仍然保留一个 .log 文件,django 进程在其中写入,并且它不会旋转并因错误而崩溃......
我见过很多类似的问题,但没有找到适合我的解决方案。各地的信息不同,到处都有不同的选择,在我看来,任务相当琐碎,肯定有一个好的解决方案。
提前致谢。
UPD.1:我怀疑 celery 以某种方式加载了 LOGGING 中指定的设置。当我os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'company.settings')
从 celery.py 中删除该行时,工作进程不再保留 django 进程写入的 .log 文件(当然,任务不是自动找到的,我这样做只是为了检查)。
UPD.2:
启动 django 服务器:python manage.py runserver --noreload
.
启动芹菜工人:(celery -A company worker --loglevel=info -P eventlet
我使用的是win10)。