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)。
我找到了我的问题的答案!
一般来说,重点是当我们
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'company.settings')向 celery.py 写入一行代码时,celery 工作进程会从 settings.py 接收带有所有 django 和 LOGGING 设置的日志记录模块(它是一个单例) 。因此,django 进程和 celery worker 有共同的日志设置(celery 只是添加了自己的)。因此,他们窃取了彼此的日志文件。所以这是实际的解决方案。问题是如果文件被另一个进程占用,Windows 禁止重命名文件(在日志轮换期间),但 Linux 不是:) ohm。怎么会有危险
就我而言,我为每个进程都有自己的日志文件,这是合乎逻辑且正确的。但是,一个进程可以删除/重命名另一个进程当前正在使用的文件。另一个进程不会知道它,并且可能会出现大问题。但是,如果这些进程只处理自己的文件,而不接触其他文件,那么就不会有问题。