Django Celery Logging最佳实践

ntjbwcob  于 2023-04-13  发布在  Go
关注(0)|答案(4)|浏览(183)

我正在尝试让Celery日志记录与Django一起工作。我在settings.py中设置了日志记录,以便转到控制台(由于我在Heroku上托管,因此工作正常)。在每个模块的顶部,我有:

import logging
logger = logging.getLogger(__name__)

在我的www.example.com中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的日志条目中,因为文档似乎表明它应该)。
我想我错过了一些简单的东西。

qacovj5a

qacovj5a1#

当你的logger在“另一个模块”的开头初始化时,它会链接到另一个logger。它会处理你的消息。它可以是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)

在这种情况下,我想它应该像你假设的那样工作。
P.S. dictConfig在Python2.7+中添加。

ozxc1zmp

ozxc1zmp2#

为了解决重复日志记录的问题,我的做法是在声明设置时将propagate设置设置为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项目布局看起来像这样:
我的项目/

  • tasks.py
  • email.py
    假设你的一个任务调用了www.example.com中的某个函数email.py;日志记录将在email.py中发生,然后日志记录将传播到'parent',在这种情况下,它恰好是你的celery任务。因此,双重日志记录。但是为特定的日志记录器设置propagate为False意味着对于该日志记录器/应用程序,它的日志不会传播到父任务,因此它们将不会是“双重”日志记录。默认情况下,“propagate”设置为True
    这里有一个指向django文档部分的链接,关于父/子记录器的内容
ws51t4hk

ws51t4hk3#

也许它会帮助别人,我的问题是把所有的celery 日志发送到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
    }
}
8wigbo56

8wigbo564#

Celery会干扰根日志记录器,这很麻烦(这不是最佳实践,无法完全控制),但它不会以任何方式禁用应用的自定义记录器,因此,使用您自己的处理程序名称并定义您自己的行为,而不是试图用Celery来解决这个问题。你可以为Django代码和Celery任务使用单独的处理程序,你只需要在Django LOGGING配置中定义它们。为模块,文件名和processName添加格式化参数到格式化程序中,以帮助你区分消息的来源。
[this假设你已经在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生成的日志-使用celeryd标记--logfile将Celery输出(例如,worker init,started task,task failed)发送到一个单独的位置(如果需要)。或者,使用这里的另一个答案将'celery'日志发送到您选择的文件。
注意:我不会使用RotatingFileHandlers -它们不支持多进程应用程序。从另一个工具(如logrotate)的日志旋转更安全,假设您有多个进程,或者与celery worker共享相同的日志文件,从Django日志记录也是如此。如果您使用多服务器解决方案,您可能希望在集中的地方记录日志。

相关问题