Django芹菜日志最佳实践

问题描述 投票:64回答:4

我正试图让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的日志条目中,因为它是 文献 似乎表明它应该)。)

我怀疑我漏掉了一些简单的东西。

python django logging celery django-celery
4个回答
72
投票

当你的日志记录器在 "另一个模块 "的开头初始化时,它会链接到另一个日志记录器。它处理你的消息。它可以是根记录器,或者通常我在Django项目中看到的--记录器的名字是 ''.

最好的方法是覆盖你的日志配置。

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)

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

P.S. 在Python2.7+中添加了dictConfig。


8
投票

Celery干扰根日志器是很麻烦的(这不是最佳实践,也无法完全控制),但它不会以任何方式禁用你的应用程序的自定义日志器,所以使用你自己的处理程序名称并定义你自己的行为,而不是试图用Celery来解决这个问题。反正我喜欢把我的应用程序的日志记录分开)。 你可以使用单独的处理程序,或者对Django代码和Celery任务使用相同的处理程序,你只需要在你的Django LOGGING配置中定义它们。 为了保证安全性,在formatter中添加模块、文件名和processName的格式化args,以帮助你区分消息的来源。

[这假定你已经在LOGGING设置值中为'yourapp'设置了一个指向Appender的处理程序--听起来你已经意识到了这一点]。

views.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,started task,task failed)发送到一个单独的地方。 或者,使用这里的另一个答案,将'celery'记录器发送到你选择的文件。

注意:我不会使用RotatingFileHandlers - 它们不支持多进程应用程序。 从另一个工具(如logrotate)进行日志轮换是比较安全的,从Django进行日志记录也是如此,假设你有多个进程,或者与芹菜工人共享相同的日志文件。 如果你使用的是多服务器的解决方案,你可能希望在某个地方集中记录。


7
投票

为了解决重复的日志问题,我的做法是在声明我的settings.LOGGING dict时,将传播设置为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中,然后日志记录将被传播到 "父 "中,在这种情况下,父就是你的芹菜任务。因此,双重日志记录。但是,如果将某一日志记录器的传播设置为 "False",则意味着对于该日志记录器来说,它的日志不会传播到父日志记录器,因此不会有 "双重 "日志记录。

这里有一个 链接到Django文档 关于父子记录器的部分


0
投票

也许能帮到大家,我的问题是把芹菜的日志都发到graylog。下面是解决方案。

celery.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()

Settings. 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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.