我已经使用他们的守护程序指令(http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#daemonizing)设置了芹菜来使用我的django应用程序
这是我的测试任务
@periodic_task(run_every=timedelta(seconds=10))
def debugger():
logger.info("Running debugger")
raise Exception('Failed')
我需要一种方法来了解此任务(调试器)由于异常而失败。 Celery的日志文件打印logger.info(“运行调试器”)日志,但它不记录异常。我错过了什么,或者我应该以其他方式找到失败的任务?
答案 0 :(得分:12)
问题:
我希望Celery能够捕获异常并将其写入日志文件,而不是明显吞下它们......
目前的最佳答案对于专业解决方案来说是一般的。许多python开发人员会根据具体情况考虑一揽子错误。在评论中明确表达了对此的合理厌恶:
坚持,我预计工作日志中会记录一些事情,至少是每个失败的任务......
Celery确实捕获了异常,它只是没有做OP想要它做的事情(它将它存储在结果后端)。以下要点是互联网在这个问题上提供的最好的。它有点过时了,但请注意叉子和星星的数量。
https://gist.github.com/darklow/c70a8d1147f05be877c3
要点是失败的情况并做一些自定义的事情。这是OP问题的超集。以下是如何调整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)
您仍然需要确保所有任务都继承自此任务类,并且如果您正在使用@task
装饰器(使用base=LogErrorsTask
kwarg),则要点显示如何执行此操作)。
此解决方案的好处是不会将代码嵌套在任何其他try-except上下文中。这是在芹菜已经使用的失败代码路径上捎带的。
答案 1 :(得分:7)
您可以查看Celery User Guide:
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 logging module的文档:
Logger.exception(msg,* args)
在此记录器上记录级别为ERROR的消息。参数被解释为debug()。异常信息将添加到日志消息中。只应从异常处理程序调用此方法。
答案 2 :(得分:3)
要接收Celery任务中所有未处理的异常,我注册了一个信号处理程序。我正在格式化logging.error
消息,然后可以通过默认的Python日志记录配置来处理它。
这是相关部分
from celery import signals
@signals.task_retry.connect
@signals.task_failure.connect
@signals.task_revoked.connect
def on_task_failure(**kwargs):
"""Abort transaction on task errors.
"""
# celery exceptions will not be published to `sys.excepthook`. therefore we have to create another handler here.
from traceback import format_tb
log.error('[task:%s:%s]' % (kwargs.get('task_id'), kwargs['sender'].request.correlation_id, )
+ '\n'
+ ''.join(format_tb(kwargs.get('traceback', [])))
+ '\n'
+ str(kwargs.get('exception', '')))
请注意,此信号处理程序会自动执行所有任务;即不需要更改您的task
装饰器。
答案 3 :(得分:1)
使用traceback模块将跟踪捕获为字符串并将其发送到记录器。
try:
...
except:
import traceback
logger.info(traceback.format_exc())
答案 4 :(得分:0)
您也可以覆盖芹菜应用,以避免向每个base
装饰者添加@app.task
kwarg:
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__)