tasks send_task periodic interval how logging celery django-celery

logging - send_task - Cómo registrar excepciones que ocurren en una tarea de apio de django



periodic tasks celery (5)

He configurado el apio para trabajar con mi aplicación django usando sus instrucciones de demonización ( http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#daemonizing )

Aquí está mi tarea de prueba

@periodic_task(run_every=timedelta(seconds=10)) def debugger(): logger.info("Running debugger") raise Exception(''Failed'')

Necesito una forma de saber que esta tarea (depurador) falló debido a la excepción. El archivo de registro de Celery imprime el registro logger.info ("ejecutando el depurador"), pero no registra la excepción. ¿Me estoy perdiendo algo, o se supone que debo encontrar tareas fallidas de otra manera?


La pregunta:

Me gustaría que Celery detecte las excepciones y las escriba en un archivo de registro en lugar de aparentemente tragarlas ...

La respuesta principal actual aquí es regular para propósitos de una solución profesional. Muchos desarrolladores de python considerarán la captura de un error general sobre una base de caso por caso una bandera roja. Una aversión razonable a esto estaba bien articulada en un comentario:

Espera, espero que haya algo registrado en el registro de trabajadores, por lo menos, para cada tarea que falle ...

El apio atrapa la excepción, simplemente no está haciendo lo que el OP quería que hiciera con él (lo almacena en el backend del resultado). La siguiente información es lo mejor que Internet tiene para ofrecer sobre este problema. Es un poco anticuado, pero tenga en cuenta el número de tenedores y estrellas.

https://gist.github.com/darklow/c70a8d1147f05be877c3

Lo esencial es tomar el caso de falla y hacer algo personalizado con él. Este es un superconjunto del problema del OP. Aquí es cómo ajustar la solución en la esencia para registrar la excepción.

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)

Aún tendrá que asegurarse de que todas sus tareas se hereden de esta clase de tareas, y el gist muestra cómo hacerlo si está usando el decorador @task (con la base=LogErrorsTask kwarg).

El beneficio de esta solución es no anidar su código en ningún contexto de prueba-excepción adicional. Esto se lleva a cuestas en la ruta del código de falla que el apio ya está usando.


Para recibir todas las excepciones no controladas de las tareas de apio, registré un controlador de señales. En ese momento estoy formateando un mensaje logging.error , que luego puede ser manejado por la configuración predeterminada de registro de Python.

Aquí está la parte relevante.

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'', '''')))

Tenga en cuenta que este controlador de señales funciona para todas las tareas automáticamente; Es decir, no requiere cambiar tus decoradores de task .


Puedes consultar la Guía del usuario de Celery :

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")

De la documentación para el módulo de registro de python :

Logger.exception (msg, * args)

Registra un mensaje con nivel ERROR en este registrador. Los argumentos se interpretan como para debug (). La información de excepción se agrega al mensaje de registro. Este método solo debe llamarse desde un controlador de excepciones.


También puede anular la aplicación de apio, para evitar agregar kwarg base a cada decorador @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__)


Utilice el módulo de rastreo para capturar la traza como una cadena y enviarla al registrador.

try: ... except: import traceback logger.info(traceback.format_exc())