Le céleri ne semble pas gérer correctement les exceptions.
Si j'ai tâche:
def errorTest():
raise Exception()
et puis j'appelle
r = errorTest.delay()
In [8]: r.result
In [9]: r.state
Out[9]: 'PENDING'
Et ça va pendre comme ça indéfiniment.
Aller vérifier les journaux montre que l'erreur IS est renvoyée dans la tâche (et si vous voulez le message, demandez), et je sais que le backend et tout sont correctement configurés, car d'autres tâches fonctionnent et donnent des résultats correctement.
Dois-je faire quelque chose de génial pour attraper des exceptions dans le céleri?
/ Celery version est 3.0.13, le courtier est RabbitMQ s'exécutant sur ma machine locale
Vous pouvez définir une fonction on_failure
dans votre sous-classe Task
pour les gérer correctement. Si vous cherchez seulement à savoir ce qui s'est passé, vous pouvez configurer les notifications par courrier électronique d'erreur qui vous enverra la trace de pile dans votre configuration de céleri.
Remarque: à compter de la v4, Celery n'est plus prend en charge l'envoi d'e-mails .
Si vous exécutez Celery avec CELERY_ALWAYS_EAGER défini sur True, assurez-vous d'inclure également cette ligne dans vos paramètres:
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
http://docs.celeryproject.org/en/latest/configuration.html#celery-eager-propagates-exceptions
Rendre la réponse de @ primalpython plus explicite.
Cela va échouer:
@task
def error():
raise Exception
Entrée sortie:
In [7]: r = error.delay()
In [8]: print r.state
Out[8]: 'PENDING'
In [9]: print r.result
Out[9]: None
Cela va réussir:
@task
def error():
raise Exception
def on_failure(self, *args, **kwargs):
pass
Entrée sortie:
In [7]: r = error.delay()
In [8]: print r.state
Out[8]: 'FAILURE'
In [9]: print r.result
Out[9]: Exception()
Pour ce faire, le moyen le plus simple consiste à transmettre une référence à une classe de tâches à utiliser lors de la création de votre nouvelle application Celery.
Dans un module, définissez la classe de tâches à utiliser par défaut:
from celery.app.task import Task
import logging
logger=logging.getLogger(__name__)
class LoggingTask(Task):
def on_failure(self, exc, task_id, args, kwargs, einfo):
kwargs={}
if logger.isEnabledFor(logging.DEBUG):
kwargs['exc_info']=exc
logger.error('Task % failed to execute', task_id, **kwargs)
super().on_failure(exc, task_id, args, kwargs, einfo)
Lorsque vous définissez votre application, référencez le module (remarque: il s'agit d'une référence de chaîne que vous fournissez ..):
from celery import Celery
app=Celery('my_project_name', task_cls='task_package.module_name:LoggingTask')
À partir de ce moment, si aucune classe de tâches n'est spécifiquement fournie, la tâche LoggingTask sera utilisée, vous permettant ainsi d'effectuer les tâchesall existantes (qui utilisent la valeur par défaut) plutôt que de les modifier. Cela signifie également que vous pouvez utiliser le décorateur @shared_task normalement.