IT박스

Django Celery 로깅 모범 사례

itboxs 2020. 11. 25. 07:48
반응형

Django Celery 로깅 모범 사례


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의 로그 항목에 작업 이름을 표시하는 방법을 알아낼 수 없습니다 .)

나는 여기서 간단한 것을 놓치고 있다고 생각합니다.


로거가 "다른 모듈"의 시작 부분에서 초기화되면 다른 로거에 연결됩니다. 귀하의 메시지를 처리합니다. 루트 로거 일 수도 있고 일반적으로 Django 프로젝트에서 볼 수 있습니다-logger with 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 이상에 추가되었습니다.


It is troubling that Celery interferes with the root logger (which is not best practice and can't be controlled completely), but it does not disable your app's custom loggers in any way, so use your own handler names and define your own behavior rather than trying to fix this issue with Celery. [I like to keep my application logging separate anyway). You could use separate handlers or the same for Django code and Celery tasks, you just need to define them in your Django LOGGING config. Add formatting args for module, filename, and processName to your formatter for sanity, to help you distinguish where messages originate.

[this assumes you have setup a handler for 'yourapp' in the LOGGING settings value that points to an Appender - sounds like you are aware of this though].

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')

For the logging that Celery generates - use the celeryd flags --logfile to send Celery output (eg, worker init, started task, task failed) to a separate place if desired. Or, use the other answer here that sends the 'celery' logger to a file of your choosing.

Note: I would not use RotatingFileHandlers - they are not supported for multi-process apps. Log rotation from another tool like logrotate is safer, same goes with logging from Django assuming you have multiple processes there, or the same log files are shared with the celery workers. If your using a multi-server solution you probably want to be logging somewhere centralized anyway.


To fix duplicate logging issue, what worked for me is to set the propagate setting to false when declaring my settings.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
        },
    }
}

lets say your django project layout looks like:
my_project/
- tasks.py
- email.py

and lets say one of your tasks makes a call to some function in email.py; the logging will happen in email.py and then that logging will get propagated to the 'parent' which in this case happens to be your celery task. Thus double logging. But setting propagate to False for a particular logger means that for that logger/app, its logs wont get propagated to the parent, hence their will be no 'double' logging. By default 'propagate' is set to True

Here's a link to the django docs section about that parent/children loggers stuff

참고URL : https://stackoverflow.com/questions/13366312/django-celery-logging-best-practice

반응형