로깅 구성 및 사용 방법

Django는 쉽게 확장할 수 있는 :ref:`기본 로깅 구성 <default-logging-configuration>`을 제공합니다.

기본 로깅 호출하기

코드 내에서 로그 메시지를 보내려면 로깅 호출을 입력합니다.

``settings.py``에서 로깅 호출을 사용하고 싶은 유혹에 빠지지 마십시오.

Django 로깅을 setup() 함수의 일부로 구성하면 ``settings.py``에 있는 로깅 호출이 예상대로 작동하지 않을 수 있습니다. 왜냐하면 로깅이 그 시점에서 설정되지 않기 때문입니다. 로깅을 탐색하려면 아래 예에서 제안된 뷰 함수를 사용하십시오.

먼저 Python 로깅 라이브러리를 import한 다음, logging.getLogger`를 사용하여 로거 인스턴스를 가져옵니다. ``getLogger()`() 메서드에 이를 식별할 수 있는 이름과 생성하는 레코드를 제공하십시오. 좋은 옵션은 ``__name__``(자세한 내용은 아래의 로거 네임스페이싱 사용 참조)을 사용하는 것입니다. 그러면 현재 파이썬 모듈의 이름이 점으로 구분된 경로로 제공됩니다:

import logging

logger = logging.getLogger(__name__)

모듈 수준에서 이 선언을 수행하는 것이 좋습니다.

그런 다음 함수 안(예: 뷰 안)에서 로거에 레코드를 보냅니다:

def some_view(request):
    ...
    if some_risky_state:
        logger.warning("Platform is running at risk")

이 코드가 실행되면 해당 메시지가 포함된 :py:class:`~logging.LogRecord`가 로거로 전송됩니다. Django의 기본 로깅 구성을 사용하는 경우 메시지가 콘솔에 나타납니다.

위의 예에서 사용된 WARNING 수준은 여러 로깅 심각도 수준들 <topic-logging-parts-loggers>`중 하나입니다: ``DEBUG`, INFO, WARNING, ERROR, CRITICAL. 따라서 다른 예는 다음과 같습니다:

logger.critical("Payment system is not responding")

중요

``WARNING``보다 낮은 수준의 레코드는 기본적으로 콘솔에 나타나지 않습니다. 이 동작을 변경하려면 추가 구성이 필요합니다.

로깅 구성 사용자 정의

Django의 로깅 구성은 별 다른 구성 없이도 작동하지만, 몇 가지 추가 구성을 통해 로그가 다양한 대상(로그 파일, 외부 서비스, 이메일 등)으로 전송되는 방식을 정확하게 제어할 수 있습니다.

다음을 설정할 수 있습니다:

  • 어떤 레코드를 어떤 핸들러로 보낼지 결정하는 로거 매핑

  • 받은 레코드로 무엇을 할지 결정하는 핸들러

  • 레코드 전송에 대한 추가 제어를 제공하고 레코드를 바로 수정할 수 있는 필터

  • LogRecord 객체를 사람이나 다른 시스템에서 사용할 문자열이나 다른 형식으로 변환하는 포맷터

로깅 구성에는 다양한 방법들이 있습니다. Django에서는 LOGGING 구성이 가장 일반적으로 사용됩니다. 이 구성은 :ref:`dictConfig 형식 <logging-config-dictschema>`을 사용하고 :ref:`기본 로깅 구성 <default-logging-definition>`을 확장합니다.

사용자 지정 설정이 Django의 기본값과 병합되는 방법에 대한 설명은 :ref:`configuring-logging`을 참조하세요.

로깅을 구성하는 다른 방법에 대한 자세한 내용은 mod:Python 로깅 문서 <python:logging.config>`를 참조하세요. 단순화를 위해 이 문서에서는 ``LOGGING` 설정을 통한 구성만 고려합니다.

기본 로깅 구성

로깅을 구성할 때, 다음을 수행하는 것이 좋습니다:

LOGGING 사전 생성

``settings.py``에서:

LOGGING = {
    "version": 1,  # the dictConfig format version
    "disable_existing_loggers": False,  # retain the default loggers
}

``disable_existing_loggers``를 ``False``로 설정하여 기본 로깅 구성을 유지하고 확장하는 것이 거의 항상 좋습니다.

핸들러 구성

이 예제는 Python의 FileHandler`를 사용하여 ``DEBUG` 이상의 로그를 (프로젝트 루트에 있는) general.log 파일에 저장하는 ``file``이라는 단일 핸들러를 구성합니다:

LOGGING = {
    # ...
    "handlers": {
        "file": {
            "class": "logging.FileHandler",
            "filename": "general.log",
        },
    },
}

다른 핸들러 클래스는 다른 구성 옵션을 사용합니다. 사용 가능한 핸들러 클래스에 대한 자세한 내용은 Django에서 제공하는 클래스:~django.utils.log.AdminEmailHandler 및 Python에서 제공하는 다양한 핸들러 클래스 <logging.handlers>`를 참조하세요.

로깅 수준은 핸들러에서도 설정할 수 있습니다(기본적으로 모든 수준의 로그 메시지를 수락함). 위의 예를 사용하여 다음을 추가합니다:

{
    "class": "logging.FileHandler",
    "filename": "general.log",
    "level": "DEBUG",
}

DEBUG 수준 이상의 레코드만 허용하는 핸들러 구성을 정의합니다.

로거 매핑 설정

이 핸들러로 레코드를 보내려면, 로거 매핑을 구성합니다. 예를 들면 다음과 같습니다:

LOGGING = {
    # ...
    "loggers": {
        "": {
            "level": "DEBUG",
            "handlers": ["file"],
        },
    },
}

매핑의 이름은 처리할 로그 레코드를 결정합니다. 이 구성('')은 unnamed*입니다. 즉, *모든 로거의 레코드를 처리한다는 의미입니다(레코드를 처리할 로거를 결정하기 위해 매핑 이름을 사용하는 방법은 아래의 로거 네임스페이싱 사용 참조).

DEBUG 수준 이상의 메시지를 ``file``이라는 핸들러로 전달합니다.

로거는 여러 핸들러에 메시지를 전달할 수 있으므로, 로거와 핸들러 간의 관계는 many-to-many입니다.

만약 다음을 실행하는 경우:

logger.debug("Attempting to connect to API")

코드에서 프로젝트 루트의 general.log 파일에서 해당 메시지를 찾을 수 있습니다.

포맷터 구성

기본적으로 최종 로그 출력에는 각 로그 레코드 <logging.LogRecord>`의 메시지 부분이 포함됩니다. 추가 데이터를 포함하려면 포맷터를 사용하십시오. 먼저 이름을 지정하고 포맷터를 정의합니다. 이 예에서는 verbose``simple``이라는 포맷터를 정의합니다:

LOGGING = {
    # ...
    "formatters": {
        "verbose": {
            "format": "{name} {levelname} {asctime} {module} {process:d} {thread:d} {message}",
            "style": "{",
        },
        "simple": {
            "format": "{levelname} {message}",
            "style": "{",
        },
    },
}

style 키워드를 사용하면 str.format`에 ``{``를 지정하거나 :class:`string.Template() 형식에 ``$``를 지정할 수 있습니다. 기본값은 ``$``입니다.

include할 수 있는 LogRecord 속성에 대해 :ref:`logrecord-attributes`를 참조하십시오.

포맷터를 핸들러에 적용하려면, 이름으로 포맷터를 참조하는 핸들러의 사전에 formatter 항목을 추가하십시오. 예를 들면 다음과 같습니다:

"handlers": {
    "file": {
        "class": "logging.FileHandler",
        "filename": "general.log",
        "formatter": "verbose",
    },
}

로거 네임스페이싱 사용

명명되지 않은 로깅 설정 ``’’``은 모든 Python 응용 프로그램에서 로그를 캡처합니다. 명명된 로깅 구성은 이름이 일치하는 로거에서만 로그를 캡처합니다.

로거 인스턴스의 네임스페이스는 :py:func:`~logging.getLogger`를 사용하여 정의됩니다. 예를 들어 ``my_app``의 ``views.py``에서:

logger = logging.getLogger(__name__)

my_app.views 네임스페이스에 로거를 생성합니다. ``__name__``을 사용하면 프로젝트의 애플리케이션 내에서 출처에 따라 로그 메시지를 자동으로 구성할 수 있습니다. 또한 이름 충돌이 발생하지 않도록 보장해줍니다.

``my_app.views``라는 로거 매핑은 이 로거에서 레코드를 캡처합니다:

LOGGING = {
    # ...
    "loggers": {
        "my_app.views": {...},
    },
}

my_app``이라는 이름의 로거 매핑은 ``my_app 네임스페이스(my_app.views, my_app.utils 등 포함) 내의 모든 로거에서 레코드를 캡처하기 떄문에 더 관대합니다:

LOGGING = {
    # ...
    "loggers": {
        "my_app": {...},
    },
}

다음과 같이 로거 네임스페이스를 명시적으로 정의할 수도 있습니다:

logger = logging.getLogger("project.payment")

그리고 그에 따라 로거 매핑을 설정합니다.

로거 계층 및 전파 사용

로거 이름 지정은 *계층적*입니다. my_app``은 ``my_app.views.private``의 부모인 ``my_app.views``의 부모입니다. 달리 지정하지 않는 한, 로거 매핑은 처리하는 레코드를 부모에게 전파합니다. ``my_app.views.private 네임스페이스에 있는 로거의 레코드는 my_appmy_app.views 모두에 대한 매핑에 의해 처리됩니다.

이 동작을 관리하려면, 당신이 정의한 매핑에서 전파 키를 설정합니다.

LOGGING = {
    # ...
    "loggers": {
        "my_app": {
            # ...
        },
        "my_app.views": {
            # ...
        },
        "my_app.views.private": {
            # ...
            "propagate": False,
        },
    },
}

``propagate``의 기본값은 ``True``입니다. 이 예에서 ``my_app.views.private``의 로그는 상위에서 처리되지 않지만 ``my_app.views``의 로그는 처리됩니다.

반응형 로깅 구성

로깅은 필요하지 않은 정보를 제외한 가능한 많은 정보를 포함할 때 가장 유용합니다. 필요한 정보의 양은 수행 중인 작업에 따라 다릅니다. 디버깅할 때는 프로덕션에서는 과도하고 도움이 되지 않는 수준의 정보가 필요합니다.

필요할 때 필요한 세부 수준을 제공하도록 로깅을 구성할 수 있습니다. 이를 위해 수동으로 구성을 변경하는 것보다, 환경에 따라 자동으로 구성을 적용하는 것이 더 좋은 방법입니다.

예를 들어, 개발 및 스테이징 환경에서 DJANGO_LOG_LEVEL 환경 변수를 적절하게 설정하여 로거 매핑에서 사용할 수 있습니다:

"level": os.getenv("DJANGO_LOG_LEVEL", "WARNING")

- 따라서 환경이 더 낮은 로그 수준을 지정하지 않는 한 이 구성은 WARNING 이상의 심각도를 가진 레코드만 핸들러로 전달합니다.

구성의 다른 옵션(예: 핸들러의 level 또는 formatter 옵션)도 유사하게 관리할 수 있습니다.

Back to Top