web-dev-qa-db-ja.com

Djangoセロリタスクで発生する例外をログに記録する方法

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

このタスク(デバッガー)が例外のために失敗したことを知る方法が必要です。 Celeryのログファイルはlogger.info( "running debug")ログを出力しますが、例外はログに記録されません。私は何かが足りないのですか、それとも他の方法で失敗したタスクを見つけることになっていますか?

25
zimkies

質問:

Celeryに例外をキャッチして、明らかに飲み込むのではなく、ログファイルに書き込んでもらいたいのですが...

ここでの現在のトップの答えは、プロのソリューションの目的のためにまあまあです。多くのpython開発者は、ケースバイケースで包括的エラーをキャッチすることを危険信号と見なします。これに対する合理的な嫌悪感は、コメントで明確に述べられています。

ちょっと待ってください。少なくとも、失敗するすべてのタスクについて、ワーカーログに何かが記録されると思います...

セロリは例外をキャッチします。OPが望んでいたことを実行していません(結果のバックエンドに保存します)。次の要点は、この問題に関してインターネットが提供しなければならない最高のものです。少し古いですが、フォークと星の数に注意してください。

https://Gist.github.com/darklow/c70a8d1147f05be877c

要旨は失敗事例を取り上げ、それを使って何かカスタムを行っています。これは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)

すべてのタスクがこのタスククラスから継承されていることを確認する必要があります。Gistは、@taskデコレータ(base=LogErrorsTask kwargを使用)を使用している場合にこれを行う方法を示しています。

このソリューションの利点は、コンテキストを除いて、追加の試行でコードをネストしないことです。これは、セロリがすでに使用している障害コードパスに便乗しています。

13
AlanSE

あなたは見ることができます 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")

pythonロギングモジュール のドキュメントから:

Logger.exception(msg、* args)

このロガーにレベルERRORのメッセージをログに記録します。引数はdebug()と同様に解釈されます。例外情報がログメッセージに追加されます。このメソッドは、例外ハンドラーからのみ呼び出す必要があります。

8
Max Kamenkov

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デコレータを変更する必要はありません。

4
pansen

Tracebackモジュールを使用して、トレースを文字列としてキャプチャし、それをロガーに送信します。

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

@app.taskデコレータにbase 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__)
1