티스토리 뷰

Django

[Django] 로그 남기기 - logging

chief 2022. 7. 20. 12:00
반응형

🚀  들어가며...

  • 금일 포스팅에서는 장고의 로깅(logging)에 대해 알아보겠습니다.

 

📑 내용

장고의 로깅은 기본적으로 파이썬의 로깅 체계를 그대로 따르면서 일부만 추가되었습니다.

(파이썬의 로깅 모듈 : 로거, 핸들러, 필터, 포맷터 )

또한, 장고의 runserver나 웹 서버에 의해 장고가 실행될 때 장고는 settings.py 파일에 정의된 LOGGING_CONFIG, LOGGING 항목을 참고하여 로깅에 관련된 설정을 처리합니다.

settings.py 파일에 관련 항목이 없더라도 디폴트 로깅 설정으로 처리됩니다. 따라서 장고의 로깅은 실행되는 시점부터 준비되어 있으므로, 항상 로그를 기록하는 것이 가능한 상태입니다.

# 로거

로거(Logger): 로깅 시스템의 시작점으로, 로그 메시지를 처리하기 위해 메시지를 담아두는 저장소  입니다.

  • 모든 로거는 이름을 갖고 있습니다.
  • 로거는 로그 레벨을 갖고 있습니다. (로그 메시지의 중요도에 따라 자신이 어느 레벨 이상의 메시지를 처리할지에 대한 기준이 됩니다.)

 

파이썬의 로그 레벨

  • DEBUG : 디버그 용도로 사용되는 정보로, 로그 레벨의 최하위 수준
  • INFO : 일반적이고 보편적인 정보
  • WARNING : 문제점 중에서 덜 중요한 문제점이 발생 시 이에 대한 정보
  • ERROR : 문제점 중에서 주요 문제점이 발생 시 이에 대한 정보
  • CRITICAL : 치명적인 문제점이 발생 시 이에 대한 정보로, 로그 레벨의 최상위 수준

 로그 레코드 역시 로그 레벨을 갖고 있습니다. (로그 레코드 : 로거에 저장되는 메시지) 또한, 로그 레코드는 스택 트레이스 정보나 에러 코드 등의 로그 이벤트에 대한 유용한 메타 정보도 갖고 있습니다.

 

메시지가 로거에 도착하면, 로그 레코드의 로그 레벨과 로거의 로그 레벨을 비교합니다.

  • 로그 레코드의 로거 레벨 >= 로거의 로그 레벨 : 로그 레코드 메시지 처리를 계속 진행
  • 로그 레코드의 로거 레벨 < 로거의 로그 레벨 : 메시지는 무시 

 

# 핸들러

핸들러 : 로거에 있는 메시지에 무슨 작업을 할지 결정하는 엔진.

  • 메시지를 화면이나 파일 또는 네트워크 소켓 등 어디에 기록할 것인지와 같은 로그 동작을 정의합니다.
  • 로그 레벨을 가집니다.
  • 로그 레코드의 로그 레벨 < 핸들러의 로그 레벨 : (핸들러는)메시지 무시
  • 로거는 핸들러를 여러 개 가질 수 있고, 각 핸들러는 서로 다른 로그 레벨을 가질 수 있습니다. - 메시지의 중요도에 따라 다른 방식의 로그 처리가 가능합니다.

 

# 필터

로그 레코드가 로거에서 핸들러로 넘겨질 때, 필터를 사용해서 로그 레코드에 추가적인 제어를 할 수 있습니다.

기본 처리 방식

: 로그 레벨을 지정하여 그 로그 레벨에 해당되면 관련 로그 메시지를 처리하는 것.

  • (필터 적용시)로그 처리 기준을 추가할 수 있습니다.

예) 필터를 추가하여 ERROR 메시지 중에서 특정 소스로부터 오는 메시지만 핸들러로 넘길 수 있습니다.

  • 레그 레코드를 보내기 전 수정하는 것이 가능합니다.

예) 어떤 조건에 맞으면 ERROR 로그 레코드를 WARNING 로그 레벨로 낮춰주는 필터를 만들 수 있습니다.

  • 로거 또는 핸들러 어디에나 적용 가능하고, 여러 개의 필터를 체인 방식으로 동작시킬 수 있습니다.

 

# 포맷터

로그 레코드는 최종적으로 텍스트로 표현되는데, 포맷터는 텍스트로 표현 시 정확한 포맷을 지정해줍니다.

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

 (PyFormat 참고사이트 : https://pyformat.info/ )

 

# 로거 사용 및 로거 이름 계층화

# 파이썬의 logging 모듈을 임포트
import logging

# 로거 인스턴스를 취득함
logger = logging.getLogger(__name__)


def my_view(request, arg1, argN):
    # 필요한 로직
    if bad_mojo:
        # ERROR 레벨 이상의 로그 레코드를 기록함
        logger.error('Something went wrong!')​

 

  • logging.getLogger() : 로그 객체 가져오기(로그 객체는 이름을 가지며, 로거를 계층화할 때 이름으로 각 로거를 구분)
  • __name__ : 로거를 담고 있는 파이썬 모듈의 이름

=> 로깅 호출, 모듈 단위로 처리

 

도트 방식의 로거 이름
# 로거 이름으로 계층화
logger = logging.getLogger('project.interesting.stuff')
 

project.interesting.stuff 로거의 부모 : project.interesting 로거

 

 로거의 계층화의 중요성

: 로깅 호출은 부모 로거에게 전파됩니다.

-> 로거 트리의 최상단 루트 로거에게 핸들러 집합 하나만을 만들어도 하위 로거의 모든 로깅 호출을 잡을 수 있습니다.

예) project 네임스페이스에 정의된 로그 핸들러는 project.interesting 로거 및 project.interesting.stuff 로거가 보내주는 모든 로그 메시지를 잡을 수 있습니다.

-> 로깅 호출의 전파는 로거 단위로 제어 가능, 특정 로거에서 상위 로거로 전파되는 것을 원하지 않으면 전파 기능을 비활성화시킬 수도 있습니다.

 

(로거 객체가 갖는)로깅 호출 메소드

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

 

# 로깅 설정

  • Django settings.py 설정하기
project/settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,      #디폴트 : True, 장고의 디폴트 로그 설정을 대체. / False : 장고의 디폴트 로그 설정의 전부 또는 일부를 다시 정의
    'formatters': {                        # message 출력 포맷 형식
        'verbose': {
            'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
            'datefmt' : "%d/%b/%Y %H:%M:%S"
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': 'file_name.log',        # message가 저장될 파일명(파일명 변경 가능)
            'formatter': 'verbose'
        },
    },
    'loggers': {
        'django': {
            'handlers':['file'],        # 'file' : handler의 이름
            'propagate': True,         
            'level':'DEBUG',            # DEBUG 및 그 이상의 메시지를 file 핸들러에게 보내줍니다.
        },
        'app_name': {                   # Project에서 생성한 app의 이름
            'handlers': ['file'],          # 다른 app을 생성 후 해당 app에서도
            'level': 'DEBUG',          # 사용하고자 할 경우 해당 app 이름으로
        },                                      # 좌측 코드를 추가 작성해서 사용
    }
}​

 

  • 로깅 설정을 한 후, 로그를 사용하는 방법도 기본 방법과 같습니다.
import logging
logger = logging.getLogger(__name__)
logger.debug("--message--")​

 

  • example
logger.error("test-error")
logger.debug("test-debug")
logger.info("test-info")
 

 
 

🙋🏻‍♂️ 후기

보통 실무에서는 성공 및 실패에 대한 디버깅을 하기 위해 로깅을 많이 사용하는것 같아서 개념위주로 정리해보았습니다. 제가 실제로 로깅 코드부분을 건드릴 상황은 생기지 않겠지만, 개념과 예제정도로만 정리해도 도움이 될것 같습니다.

 

🔗  참고한 글

https://docs.python.org/2/howto/logging-cookbook.html

 

Logging Cookbook — Python 2.7.18 documentation

This page contains a number of recipes related to logging, which have been found useful in the past. Using logging in multiple modules Multiple calls to logging.getLogger('someLogger') return a reference to the same logger object. This is true not only wit

docs.python.org

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함