72

我正在尝试让 Celery 日志记录与Django. 我已经登录设置settings.py进入控制台(在我托管时工作正常Heroku)。在每个模块的顶部,我有:

import logging
logger = logging.getLogger(__name__)

在我的tasks.py中,我有:

from celery.utils.log import get_task_logger
logger = get_task_logger(__name__)

这适用于记录来自任务的调用,我得到如下输出:

2012-11-13T18:05:38+00:00 app[worker.1]: [2012-11-13 18:05:38,527: INFO/PoolWorker-2] Syc feed is starting

但是,如果该任务然后调用另一个模块中的方法,例如queryset方法,我会得到重复的日志条目,例如

2012-11-13T18:00:51+00:00 app[worker.1]: [INFO] utils.generic_importers.ftp_processor process(): File xxx.csv already imported. Not downloaded
2012-11-13T18:00:51+00:00 app[worker.1]: [2012-11-13 18:00:51,736: INFO/PoolWorker-6] File xxx.csv already imported. Not downloaded

我想我可以使用

CELERY_HIJACK_ROOT_LOGGER = False

只使用Django日志记录,但是当我尝试它时这不起作用,即使我确实让它工作,我也会失去"PoolWorker-6"我想要的位。(顺便说一句,我不知道如何让任务名称显示在 Celery 的日志条目中,因为文档似乎表明它应该这样做)。

我怀疑我在这里遗漏了一些简单的东西。

4

4 回答 4

80

当您的记录器在“另一个模块”的开头初始化时,它会链接到另一个记录器。哪个处理您的消息。它可以是根记录器,或者通常我在 Django 项目中看到的 - 带有 name 的记录器''

最好的方法是覆盖您的日志记录配置:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'simple': {
            'format': '%(levelname)s %(message)s',
             'datefmt': '%y %b %d, %H:%M:%S',
            },
        },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'celery': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'celery.log',
            'formatter': 'simple',
            'maxBytes': 1024 * 1024 * 100,  # 100 mb
        },
    },
    'loggers': {
        'celery': {
            'handlers': ['celery', 'console'],
            'level': 'DEBUG',
        },
    }
}

from logging.config import dictConfig
dictConfig(LOGGING)

在这种情况下,我想它应该像你想象的那样工作。

PS dictConfig 在 Python2.7+ 中添加。

于 2012-11-17T10:53:03.503 回答
11

要解决重复日志记录问题,对我有用的是在声明我的 settings.LOGGING 字典时将传播设置设置为 false

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose'
        },
    },
    'formatters': {
        'verbose': {
            'format': '%(asctime)s %(levelname)s module=%(module)s, '
            'process_id=%(process)d, %(message)s'
        }
    },
    'loggers': {
        'my_app1': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False #this will do the trick
        },
        'celery': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True
        },
    }
}

假设您的 django 项目布局如下所示:
my_project/
- tasks.py
- email.py

假设您的一项任务调用了 email.py 中的某个函数;日志记录将发生在 email.py 中,然后该日志记录将传播到“父级”,在这种情况下,这恰好是您的 celery 任务。因此双重记录。但是将特定记录器的传播设置为 False 意味着对于该记录器/应用程序,其日志不会传播到父级,因此它们将不会是“双重”记录。默认情况下,“传播”设置为 True

这是django 文档部分关于父/子记录器内容的链接

于 2015-07-06T17:08:29.497 回答
8

令人不安的是,Celery 会干扰根记录器(这不是最佳实践,无法完全控制),但它不会以任何方式禁用您应用的自定义记录器,因此请使用您自己的处理程序名称并定义您自己的行为而不是而不是试图用 Celery 解决这个问题。[无论如何,我喜欢将我的应用程序日志记录分开)。您可以为 Django 代码和 Celery 任务使用单独的处理程序或相同的处理程序,您只需要在 Django LOGGING 配置中定义它们。将模块、文件名和 processName 的格式化参数添加到格式化程序中,以帮助您区分消息的来源。

[这假设您已经在指向 Appender 的 LOGGING 设置值中为 'yourapp' 设置了一个处理程序 - 听起来您虽然知道这一点]。

视图.py

log = logging.getLogger('yourapp')
def view_fun():
    log.info('about to call a task')
    yourtask.delay()

任务.py

log = logging.getLogger('yourapp')
@task
def yourtask():
    log.info('doing task')

对于 Celery 生成的日志记录 - 如果需要,使用 celeryd 标志 --logfile 将 Celery 输出(例如,worker init、启动任务、任务失败)发送到单独的位置。或者,使用此处的其他答案将“芹菜”记录器发送到您选择的文件。

注意:我不会使用 RotatingFileHandlers - 多进程应用程序不支持它们。来自另一个工具(如 logrotate)的日志轮换更安全,假设您在那里有多个进程,或者与 celery 工作人员共享相同的日志文件,来自 Django 的日志记录也是如此。如果您使用多服务器解决方案,您可能希望无论如何都希望在某个集中的地方进行日志记录。

于 2012-11-22T01:00:33.060 回答
7

也许它会对某人有所帮助,我的问题是将所有芹菜日志发送到 graylog。这是解决方案。

芹菜.py:

app.config_from_object('django.conf:settings', namespace='CELERY')


# ====== Magic starts
from celery.signals import setup_logging

@setup_logging.connect
def config_loggers(*args, **kwargs):
    from logging.config import dictConfig
    from django.conf import settings
    dictConfig(settings.LOGGING)
# ===== Magic ends


# Load task modules from all registered Django app configs.
app.autodiscover_tasks()

设置.py:

LOGGING = {
    'version': 1,
    'handlers': {
        'graypy': {
            'class': 'graypy.GELFTCPHandler',
            'host': GRAYLOG_HOST,
            'port': GRAYLOG_PORT,
        }
    },
    'loggers': {
        'my_project': {
            'handlers': ['graypy'],
            'level': 'INFO',
        },
        # ====== Magic starts
        'celery': {
            'handlers': ['graypy'],
            'level': 'INFO',
        }
        # ===== Magic ends
    }
}
于 2020-06-02T19:34:37.507 回答