Есть сервис на Django (DRF), в котором настроена подсистема логирования. Также в сервисе есть Celery-воркер, который выполняет некоторую задачу. Я хочу настроить логирование таким образом, чтобы все логи (и Django, и Celery) сохранялись в .log-файлах, при этом чтобы файлы "прокручивались" с помощью RotatingFileHandler, чтобы не засорять дисковое пространство. Сразу скажу, что я новичок в логировании в Django.
Настройки LOGGING в файле settings.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,
},
'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-воркер, в celery-воркер в свою очередь держит .log-файл, в который пишет процесс django. "Прокручивание" (ротация) .log-файлов осуществляется через создании пустого .log-файла, в который пишутся логи, и
переименовании старого .log-файла. Вот в этом-то и есть проблема. Ни один процесс не может переименовать файл из-за того, что он используется другим процессом.
Что я пробовал сделать. Я пробовал полностью разделить настройки логирования celery и django, убрав все настройки для celery из settings.py и прописав их в celery.py, чтобы два процесса вообще никак не трогали файлы друг друга.
settings.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'],
},
},
}
celery.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 больше не держал .log-файл, в который пишет воркер, и тот успешно вращался! Только вот почему-то celery-воркер до сих пор держит .log-файл, в который пишет процесс django, и тот не вращается и падает с ошибкой...
Я подозреваю, что все-таки celery как-то загружает в себя настройки, которые прописаны в LOGGING.
Когда я убрал строку
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'company.settings')
из celery.py, процесс воркера больше не держал .log-файл, в который пишет процесс django (разумеется, и задачи автоматически не находились, я это сделал просто ради проверки).
Запуск сервера django:
python manage.py runserver --noreload
Запуск celery-воркера:
celery -A company worker --loglevel=info -P eventlet
Я использую win10.
Я видел множество подобных проблем, но не нашел подходящего решения для себя. Информация везде разная, везде разные варианты, а задача довольно тривиальная, на мой взгляд, и наверняка должна иметь хорошее решение.