Как регистрировать исключения, возникающие в задаче django celery
Я установил сельдерей для работы с моим приложением django, используя их инструкции демонализации (http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#daemonizing)
Вот моя тестовая задача
@periodic_task(run_every=timedelta(seconds=10))
def debugger():
logger.info("Running debugger")
raise Exception('Failed')
Мне нужно знать, что эта задача (отладчик) не удалась из-за исключения. Файл журнала сельдерей печатает журнал logger.info( "работает отладчик" ), но он не регистрирует исключение. Я что-то упускаю, или я должен найти неудачные задачи каким-то другим способом?
Ответы
Ответ 1
Вы можете посмотреть Руководство пользователя сельдерея:
from celery.utils.log import get_task_logger
logger = get_task_logger(__name__)
@app.task
def div():
try:
1 / 0
except ZeroDivisionError:
logger.exception("Task error")
Из документации для модуля протоколирования python:
Logger.exception(msg, * args)
Записывает сообщение с уровнем ERROR на этом регистраторе. Аргументы интерпретируются как для debug(). Информация об исключении добавляется в сообщение регистрации. Этот метод следует вызывать только из обработчика исключений.
Ответ 2
Вопрос:
Я бы хотел, чтобы сельдерей поймал исключения и записал их в файл журнала, вместо того, чтобы, по-видимому, усвоить их...
Текущий верхний ответ здесь так-то для профессионального решения. Многие разработчики python будут рассматривать скрытую ошибку при каждом случае на красном флаге. Разумное отвращение к этому было хорошо сформулировано в комментарии:
Подождите, я бы ожидал, что в журнале работ будет что-то зарегистрировано, по крайней мере, для каждой неудавшейся задачи...
Сельдерей действительно ломает исключение, он просто не делает то, что OP хочет от него сделать (он хранит его в бэкэнде результата). Следующий пример - лучший интернет, который может предложить по этой проблеме. Он немного устарел, но обратите внимание на количество вилок и звезд.
https://gist.github.com/darklow/c70a8d1147f05be877c3
Суть в том, что он делает ошибку и делает с ней что-то обычное. Это надмножество проблемы ОП. Вот как настроить решение в gist для регистрации исключения.
import logging
logger = logging.getLogger('your.desired.logger')
class LogErrorsTask(Task):
def on_failure(self, exc, task_id, args, kwargs, einfo):
logger.exception('Celery task failure!!!1', exc_info=exc)
super(LogErrorsTask, self).on_failure(exc, task_id, args, kwargs, einfo)
Вам все равно нужно убедиться, что все ваши задачи наследуются от этого класса задач, а сам gist показывает, как это сделать, если вы используете декоратор @task
(с base=LogErrorsTask
kwarg).
Преимущество этого решения состоит в том, чтобы не встраивать ваш код в какие-либо дополнительные контексты, отличные от try-except. Это согласуется с кодом кода отказа, который сельдерей уже использует.
Ответ 3
Используйте модуль трассировки, чтобы захватить трассировку в виде строки и отправить ее в журнал.
try:
...
except:
import traceback
logger.info(traceback.format_exc())
Ответ 4
Вы также можете переопределить приложение celery, чтобы не добавлять base
kwarg к каждому декоратору @app.task
:
import logging
from celery import Celery, Task
logger = logging.getLogger(__name__)
class LoggingTask(Task):
def on_failure(self, exc, task_id, args, kwargs, einfo):
logger.exception('Task failed: %s' % exc, exc_info=exc)
super(LoggingTask, self).on_failure(exc, task_id, args, kwargs, einfo)
class LoggingCelery(Celery):
def task(self, *args, **kwargs):
kwargs.setdefault('base', LoggingTask)
return super(LoggingCelery, self).task(*args, **kwargs)
app = LoggingCelery(__name__)