로깅 구성 및 사용 방법¶
더 보기
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_app
및 my_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
옵션)도 유사하게 관리할 수 있습니다.