본문 바로가기
  • 실행력이 모든걸 결정한다
Django

[Django] 로깅

by 김코더 김주역 2023. 1. 24.
반응형

1. 장고의 로깅

- 파이썬의 로깅 체계를 따르면서 일부만 추가되었다.

- settings.py에 정의된 LOGGIN, LOGGIN_CONFIG 속성을 참고하여 로깅 관련 설정을 처리하는데, 이 속성이 없더라도 리폴트 로깅 설정으로 처리한다.

 

 

 

2. 파이썬의 로깅 모듈

1) 로거

- 로그 메시지를 처리하기 위해 메시지를 담아두는 곳

- 모든 로거는 이름과 로그 레벨을 갖고 있으며, 로그 레벨은 어느 레벨 이상의 메시지를 처리할지에 대한 기준이다.

  • NOTSET (정수 값 0) : 리폴트 로그 레벨
  • DEBUG (정수 값 10) : 디버그 용도로 사용
  • INFO (정수 값 20) : 일반적인 정보
  • WARNING (정수 값 30) : 주의할 만한 문제점에 대한 정보
  • ERROR (정수 값 40) : 주요 문제점에 대한 정보
  • CRITICAL (정수 값 50) : 치명적인 문제점에 대한 정보

- 로거에 저장되는 메시지를 로그 레코드라고 하며, 이 또한 로그 레벨을 가진다. 로그 레코드는 로그 이벤트에 대해 스택 트레이스 정보나 에러 코드와 같은 메타 정보도 가질 수 있다.

- 한 개의 로거에 여러 개의 핸들러를 지정할 수 있다.

- 동작 방식 : 메시지가 로거에 도착하면 로거와 로그 레코드의 로그 레벨을 비교해서 로그 레코드 >= 로거 이면 메시지를 핸들러에게 넘겨주고, 로그 레코드 < 로거 이면 메시지를 무시한다.

 

 

2) 핸들러

- 로거로부터 받은 메시지에 무슨 작업을 할지 결정하는 엔진

- 메시지를 어디에 기록할 것인지와 같은 로그 동작을 정의한다.

※ 자주 사용되는 설정

  • 로그 메시지를 표준 에러로 출력 ("class": "logging.StreamHandler")
  • 로그 메시지를 파일에 기록 ("class": "logging.FileHandler")
  • 로그 메시지를 사이트 관리자의 이메일로 전송 ("class": "django.utils.log.AdminEmailHandler")

- 핸들러도 로그 레벨을 가지고 있는데, 로그 레벨 로그 레코드 < 핸들러 이면 메시지를 무시한다.

 

 

3) 필터

- 메시지가 로거에서 핸들러로 넘겨지는 과정에 개입하여 로그 처리 기준을 추가하거나 메시지를 수정하는 등의 추가적인 제어를 수행해주는 것

- 로거와 핸들러 양쪽에 적용 가능하며, 필터 체인도 가능하다.

 

 

4) 포맷터

- 로그 레코드 텍스트의 포맷을 지정해준다.

- 보통 파이썬의 포맷 스트링을 사용하지만, 사용자 정의 포맷터도 만들 수 있다.

 

 

 

3. 로거 사용

- 로거, 핸들러, 필터, 포맷터 등을 settings.py에 설정하고 코드 내에서 로깅 메소드를 호출하면 된다.

[settings.py 예시]

- 장고는 사전형 설정(dictConfig) 방식을 디폴트로 로깅을 설정한다.

# https://docs.djangoproject.com/ko/4.1/topics/logging/
# settings.py

...

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'mylogger' : {
            'handlers': ['console'],
            'level': 'WARNING',
        },
    },
}

[로깅 메소드 호출 예시]

# views.py
import logging

logger = logging.getLogger("mylogger") # settings.py에 정의한 로거의 이름으로 로거를 취득

def my_view(request, arg):
    # 로직
    if bad_mojo:
        logger.error('Something went wrong') # ERROR 레벨의 로그 레코드를 생성

 

1) 로거의 계층화

- 로거를 취득할 때 __name__ 구문을 사용할 수도 있다. __name__은 이 구문이 있는 파일의 파이썬 모듈 경로다. 로거를 계층화하면 로깅 호출을 모듈 단위로 처리할 수 있게 된다.

logger = logging.getLogger(__name__) # __name__ -> 'a.b.views'

a.b.views 로거의 부모는 a.b 로거이고, a.b 로거의 부모는 a 로거가 된다. 로거의 이름을 빈 문자열로 지정하면 루트 로거를 의미하게 된다.

- 로거의 계층화를 사용하면 기본적으로 로깅 호출을 부모 로거에게 전파시킨다. 이렇게 하면 루트 로거에서 핸들러 하나만을 만들어도 하위 로거의 모은 로깅 호출을 잡을 수 있다. 특정 로거에서 상위 로거로 전파되는 것을 원하지 않으면 다음과 같이 전파 기능을 비활성화시킬 수도 있다.

'myproject.custom': {
    ...
    'propagate': False,
},

 

 

2) 로거 객체의 로깅 호출 메소드

  • logger.debug() : DEBUG 레벨의 로그 레코드를 생성
  • logger.info() : INFO 레벨의 로그 레코드를 생성
  • logger.warning() : WARNING 레벨의 로그 레코드를 생성
  • logger.error() : ERROR 레벨의 로그 레코드를 생성
  • logger.critical() : CRITICAL 레벨의 로그 레코드를 생성
  • logger.log() : 원하는 로그 레벨을 정해서 로그 레코드를 생성
  • logger.exception() : 익셉션 스택 트레이스 정보를 포함하는 ERROR 레벨의 로그 메시지를 생성

 

 

 

4. 장고의 디폴트 로깅 설정

- 장고의 디폴트 로깅 설정은 django/utils/log.py에서 확인할 수 있다.

DEFAULT_LOGGING = {
    "version": 1, # 설정 버전 1
    "disable_existing_loggers": False, # 기존의 로거들을 비활성화하지 않음
    "filters": {
        "require_debug_false": { # DEBUG=False인 경우에만 핸들러가 동작
            "()": "django.utils.log.RequireDebugFalse", # ()는 장고에서 별도로 정의한 클래스라는 의미
        },
        "require_debug_true": { # DEBUG=True인 경우에만 핸들러가 동작
            "()": "django.utils.log.RequireDebugTrue",
        },
    },
    "formatters": {
        "django.server": { # 로그 생성 시각과 로그 메시지만을 출력
            "()": "django.utils.log.ServerFormatter",
            "format": "[{server_time}] {message}",
            "style": "{",
        }
    },
    "handlers": {
        "console": { # INFO 레벨 이상의 메시지를 표준 에러로 출력, require_debug_true 필터 사용
            "level": "INFO",
            "filters": ["require_debug_true"],
            "class": "logging.StreamHandler",
        },
        "django.server": { # INFO 레벨 이상의 메시지를 표준 에러로 출력, django.server 포맷터 사용
            "level": "INFO",
            "class": "logging.StreamHandler",
            "formatter": "django.server",
        },
        "mail_admins": { # ERROR 레벨 이상의 메시지를 사이트 관리자에게 이메일로 전송, require_debug_false 필터 사용
            "level": "ERROR",
            "filters": ["require_debug_false"],
            "class": "django.utils.log.AdminEmailHandler",
        },
    },
    "loggers": {
        "django": { # INFO 레벨 이상의 메시지를 console 및 mail_admins 핸들러로 전송
            "handlers": ["console", "mail_admins"],
            "level": "INFO",
        },
        "django.server": { # INFO 레벨 이상의 메시지를 django.server 핸들러로 전송, 상위 로그로 메시지를 전파하지 않음
            "handlers": ["django.server"],
            "level": "INFO",
            "propagate": False,
        },
    },
}

※ django.server 로거는 runserver에서 사용하는 로거다. 5xx 응답은 ERROR 메시지로, 4xx 응답은 WARNING 메시지로, 그 외는 INFO 메시지로 출력된다.

 

 

 

5. 개발자가 로깅 설정하기

- settings.py에서 LOGGING 항목을 사용하면 된다. 여기에 로거를 새롭게 추가할 수도 있고, 이미 디폴트로 설정된 로거들을 오버라이딩하여 동작을 변경할 수도 있다.

 

 

 

6. 디폴트 로깅 설정 무시

- 장고에서 이미 정의한 로거 설정을 무시하고 싶다면 LOGGING_CONFIG를 None으로 지정해주면 된다. 그리고 자신만의 로깅 설정을 LOGGING 항목에 작성하고, logging.config의 dictConfig() 함수를 통해 LOGGING에 정의된 내용을 설정하면 된다.

LOGGING_CONFIG=None
LOGGING = {
    ...
}

import logging.config
logging.config.dictConfig(LOGGING)

 

 

 

7. 장고 패키지에 추가된 그 외의 사항

([4. 장고의 디폴트 로깅 설정]에서 이미 설명한 사항은 생략한다)

 

1) 로거

- django.request : 요청 처리 관련 메시지를 기록한다. 이 로거에 담기는 메시지는 추가적으로 status_code, request 메타 항목을 가진다.

- django.template : 템플릿 렌더링 과정에서 발생하는 로그 메시지를 기록한다.

- django.db.backends : DB 관련 메시지를 기록한다. 이 로거에 담기는 메시지는 추가적으로 duration, sql, params 메타 항목을 가진다. SQL 로깅은 settings.DEBUG 속성이 True인 경우에만 활성화된다.

- django.security.* : 사용자가 보안 측면에서 위험한 동작을 실행한 경우, 이에 대한 메시지를 기록한다. 예를 들어, HTTP Host 헤더가 ALLOWED_HOSTS에 없는 경우에 반응한다.

- django.db.backends.schema : DB 스키마 변경 시 사용된 SQL 쿼리를 기록한다.

 

 

2) 필터

- CallBackFilter : 콜백 함수를 지정해서 필터를 통과하는 모든 메시지에 대해 콜백 함수를 호출한다. 콜백 함수의 리턴값이 False이면 메시지 로깅은 더 이상 처리하지 않는다.

 

 

● 장고 로깅 공식 문서 : https://docs.djangoproject.com/en/4.1/howto/logging/#customize-logging-configuration

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

반응형

댓글