Django Celery采伐最佳实践

ala*_*lan 64 python django logging celery django-celery

我正在努力让Celery登录工作Django.我有登录设置settings.py进入控制台(这是我正在托管的工作正常Heroku).在每个模块的顶部,我有:

import logging
logger = logging.getLogger(__name__)
Run Code Online (Sandbox Code Playgroud)

在我的tasks.py中,我有:

from celery.utils.log import get_task_logger
logger = get_task_logger(__name__)
Run Code Online (Sandbox Code Playgroud)

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

2012-11-13T18:05:38+00:00 app[worker.1]: [2012-11-13 18:05:38,527: INFO/PoolWorker-2] Syc feed is starting
Run Code Online (Sandbox Code Playgroud)

但是如果该任务然后调用另一个模块中的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
Run Code Online (Sandbox Code Playgroud)

我想我可以用

CELERY_HIJACK_ROOT_LOGGER = False
Run Code Online (Sandbox Code Playgroud)

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

我怀疑我在这里缺少一些简单的东西.

Rus*_*tem 70

当您的记录器在"另一个模块"的开头初始化时,它会链接到另一个记录器.哪个处理你的消息.它可以是root logger,或者通常我在Django项目中看到 - 带有名称的logger ''.

这里最好的方法是覆盖你的日志配置:

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)
Run Code Online (Sandbox Code Playgroud)

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

PS dictConfig在Python2.7 +中添加.


pav*_*roo 10

也许它会对某人有所帮助,我的问题是将所有芹菜日志发送到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()
Run Code Online (Sandbox Code Playgroud)

设置.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
    }
}
Run Code Online (Sandbox Code Playgroud)


Lin*_*n B 8

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

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

views.py

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

tasks.py

log = logging.getLogger('yourapp')
@task
def yourtask():
    log.info('doing task')
Run Code Online (Sandbox Code Playgroud)

对于芹菜产生的日志 - 使用celeryd标志--logfile送芹菜输出如果需要的话(例如,工人的init,开始任务,任务失败)到一个单独的地方.或者,使用这里的其他答案发送的"芹菜"记录到您选择的文件.

注:我不会用RotatingFileHandlers - 他们不支持多进程应用程序.来自另一个工具(如logrotate)的日志轮换更安全,与Django的日志记录相同,假设您有多个进程,或者与芹菜工作者共享相同的日志文件.如果您使用多服务器解决方案,您可能希望无论如何都要集中登录.


Kom*_*omu 7

要修复重复的日志记录问题,对我来说有用的是在声明我的设置时将传播设置设置为false.LOGGING dict

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
        },
    }
}
Run Code Online (Sandbox Code Playgroud)

让我们说你的django项目布局如下:
my_project/
- tasks.py
- email.py

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

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