Django芹菜测井最佳实践

我试图让芹菜日志与Django工作。 我有settings.py日志设置去控制台(工作正常,因为我在Heroku托pipe)。 在每个模块的顶部,我有:

 import logging logger = logging.getLogger(__name__) 

在我的tasks.py中,我有:

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

这对logging来自任务的调用工作正常,我得到这样的输出:

 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日志logging,但是当我尝试它时,这并不起作用,即使我确实得到它,我会失去我想要的"PoolWorker-6"位。 (顺便说一下,我不知道如何让任务名称显示在从芹菜的日志条目,因为文档似乎表明它应该)。

我怀疑我在这里错过了一些简单的东西。

当您的logging器在“另一个模块”的开头初始化时,它将链接到另一个logging器。 处理你的消息。 它可以是根logging器,或者通常我在Django项目中看到 – logging器名称为''

这里最好的方法是重写你的日志configuration:

 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 +中添加。

令人不安的是,Celery会干扰根logging器(这不是最佳实践,也不能完全控制),但它不会以任何方式禁用应用程序的自定义logging器,所以请使用自己的处理程序名称并定义自己的行为比试图解决与芹菜这个问题。 [我喜欢保持我的应用程序日志分开)。 您可以使用单独的处理程序或Django代码和Celery任务相同,您只需要在您的Django LOGGINGconfiguration中定义它们。 为模块,文件名和过程名添加格式化参数到您的格式化程序,以帮助您区分消息的来源。

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

views.py

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

tasks.py

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

对于Celery生成的日志logging – 如果需要,使用celeryd标志–logfile将Celery输出(例如,worker init,启动任务,任务失败)发送到单独的位置。 或者,在这里使用其他的答案,将“芹菜”logging器发送到您select的文件。

注意:我不会使用RotatingFileHandlers – 它们不支持多进程应用程序。 Logrotate等其他工具的日志转换更安全,假设您有多个进程,或者与芹菜工作者共享相同的日志文件,那么从Django进行日志logging也是如此。 如果您使用多服务器解决scheme,您可能希望在某处集中logging。

要解决重复日志logging问题,对我来说是什么工作是宣布我的设置时将传播设置为false.LOGGING字典

 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项目布局看起来像:
我的项目/
– tasks.py
– email.py

让我们说你的一个任务在email.py中调用了一些函数; 日志logging将在email.py中发生,然后logging将被传播到“父”,在这种情况下恰好是你的芹菜任务。 因此,双重logging。 但设置传播到一个特定的logging器的假意味着对于该logging器/应用程序,其日志不会传播给父,因此他们将不会“双”logging。 默认情况下,“传播”设置为“真”

这里有一个链接到django文档部分关于那个父母/孩子logging器的东西