python / / 2024. 8. 23. 08:06

Python `logging`의 이해

logging 모듈은 Python 표준 라이브러리의 일부로, 프로그램 실행 중 발생하는 다양한 이벤트를 기록하고 관리할 수 있도록 도와준다. 로그는 디버깅, 모니터링, 문제 해결, 성능 분석 등에 중요한 역할을 하며, 다양한 출력 방법을 지원해 효율적으로 로그를 관리할 수 있다.

2. 로그 레벨 (Log Level)

로그 레벨은 로깅 메시지의 중요도나 심각도를 나타낸다. logging 모듈은 다음과 같은 기본적인 로그 레벨을 제공한다:

  • DEBUG: 상세한 정보를 남기기 위해 사용됩니다. 주로 개발 중에 유용하다.
  • INFO: 일반적인 정보를 기록하며, 애플리케이션의 정상적인 동작을 알리는 데 사용된다.
  • WARNING: 경고 메시지로, 애플리케이션이 문제를 겪을 수 있지만, 계속 실행될 수 있는 상황에서 사용된다.
  • ERROR: 에러 메시지로, 애플리케이션이 제대로 작동하지 못하는 상황을 알릴 때 사용된다.
  • CRITICAL: 심각한 오류로, 프로그램의 실행이 중단될 수 있는 상황에서 사용된다.

로그 레벨을 설정하면 해당 레벨 이상의 중요도를 가진 메시지만 기록된다. 예를 들어, 로그 레벨이 WARNING으로 설정되어 있으면, WARNING, ERROR, CRITICAL 메시지만 기록되고, INFODEBUG 메시지는 무시된다.

import logging

logging.basicConfig(level=logging.INFO)

logging.debug("This is a DEBUG message")
logging.info("This is an INFO message")
logging.warning("This is a WARNING message")
logging.error("This is an ERROR message")
logging.critical("This is a CRITICAL message")

위 코드에서 INFO 레벨로 설정되어 있으므로, DEBUG 메시지는 출력되지 않는다.

logging.basicConfig의 기본값은 logging.WARN이다.

3. 파일 로깅 (File Logging)

로그를 파일에 기록하면 프로그램의 실행 중 발생하는 이벤트를 영구적으로 저장할 수 있다. 이렇게 하면 프로그램 종료 후에도 로그를 확인할 수 있어 문제 해결에 큰 도움이 된다.

로그를 파일에 기록하려면, logging.basicConfig() 함수에서 filename 매개변수를 사용하여 로그 파일의 경로를 지정할 수 있다.

import logging

logging.basicConfig(filename='app.log', level=logging.INFO)

logging.info("This is an INFO message")
logging.warning("This is a WARNING message")

이 코드를 실행하면, app.log 파일이 생성되고 로그 메시지가 파일에 기록된다.

4. 콘솔 로깅 (Console Logging)

콘솔 로깅은 로그 메시지를 터미널이나 명령 프롬프트에 출력하는 것을 의미한다. 기본적으로 logging 모듈은 로그 메시지를 콘솔에 출력하지만, StreamHandler를 사용해 콘솔 출력을 명시적으로 관리할 수도 있다.

python
코드 복사
import logging

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

logging.basicConfig(level=logging.DEBUG, handlers=[console_handler])

logging.debug("This is a DEBUG message")
logging.info("This is an INFO message")
logging.warning("This is a WARNING message")

이 코드에서는 StreamHandler를 사용해 DEBUG 레벨 이상의 모든 로그 메시지를 콘솔에 출력하도록 설정했다.

5. 로그 포맷팅 (Log Formatting)

로그 포맷팅은 로그 메시지의 형식을 정의하는 것이다. 기본 형식 외에도 로그 메시지에 타임스탬프, 로그 레벨, 파일명, 라인 번호 등을 포함할 수 있다.

포맷팅을 지정하려면 logging.basicConfig() 함수에서 format 매개변수를 사용합니다.

python
코드 복사
import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logging.debug("This is a DEBUG message")
logging.info("This is an INFO message")
logging.warning("This is a WARNING message")

위 코드에서 format 문자열은 로그 메시지가 타임스탬프, 로거 이름, 로그 레벨, 메시지 순으로 출력되도록 지정한다. 또한 datefmt 매개변수를 사용해 타임스탬프의 형식을 지정할 수 있다.

예를 들어, 위의 설정에 따라 출력된 로그 메시지는 다음과 같은 형식으로 나타날 것이다.

2024-08-18 14:30:45 - root - INFO - This is an INFO message

6. 파일과 콘솔에 동시에 로깅하기

파일과 콘솔에 로그를 동시에 기록하려면, logging 모듈의 여러 핸들러를 설정하여 사용할 수 있다.

import logging

# 로거 생성
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

# 파일 핸들러 설정
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.INFO)

# 콘솔 핸들러 설정
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 포맷 설정
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

# 핸들러 추가
logger.addHandler(file_handler)
logger.addHandler(console_handler)

# 로그 메시지
logger.debug("This is a DEBUG message")
logger.info("This is an INFO message")
logger.warning("This is a WARNING message")
logger.error("This is an ERROR message")
logger.critical("This is a CRITICAL message")

이 코드는 로그 메시지를 콘솔과 파일 모두에 기록하며, 파일에는 INFO 이상의 메시지, 콘솔에는 DEBUG 이상의 메시지를 기록한다.

7. Logger 사용하기

logger 생성

각 파일에서 로그를 남기기위해 로거 인스턴스를 생성해야 한다.

logger = logging.getLogger(__name__)
  • _name_ 은 Logger 이름이 사용된다. 비워두면 root가 들어간다.
  • _name_ 현재 로거가 생성된 네임스페이스를 따라 짓는 것이 관례이며, 마침표 ( . ) 구분자를 사용해 모듈 별 로거의 계층 구조를 표현해 주어야 한다.

logger level

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
  • logger의 level을 INFO로 설정했다.

  • logger로 출력되는 로그는 INFO 레벨 이상만 출력된다.

  • 만일 여기서 level을 설정하지 않으면(logging.NOTSET) 상위레벨, 즉 logging.basicConfig에서 설정한 레벨을 따른다.

  • 만일 로거 이름을 계층구조로 사용하였을 경우 상위 레벨을 가져온다.

    logging.basicConfig(
        level=logging.WARN,
        datefmt="%Y-%m-%d %H:%M:%S",
    )
    
    logger = logging.getLogger("base")
    logger.setLevel(logging.ERROR)
    
    logger = logging.getLogger("base.app")
    logger.setLevel(logging.NOTSET)

    이런 경우 logger는 ERROR로 설정이 된다. base.app의 계층구조에서 base로 지정된 로거의 이름이 있으므로 해당 로거의 레벨을 가져온다.

8. 로그 설정을 파일로 관리하기

1) json 파일로 관리

logger.json으로 로그 설정을 관리할 수 있다.

아래와 같이 logger.json파일을 만들고 logging.config.dictConfig를 통해 config를 불러들인다.

config = json.load(open('./logger.json'))
logging.config.dictConfig(config)

# 로거 생성
logger = logging.getLogger(__name__)

로그 설정을 할 logger.json의 내용은 아래와 같다.

{
  "version": 1,
  "disable_existing_loggers": false,
  "formatters": {
    "basic": {
      "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
      "datefmt": "%Y-%m-%d %H:%M:%S"
    }
  },
  "handlers": {
    "console": {
      "class": "logging.StreamHandler",
      "level": "DEBUG",
      "formatter": "basic"
    },
    "file": {
      "class": "logging.FileHandler",
      "level": "DEBUG",
      "formatter": "basic",
      "filename": "app.log"
    }
  },
  "root": {
    "handlers": ["console", "file"],
    "level": "WARNING"
  },
  "loggers": {
    "__main__": {
      "level": "INFO",
      "handlers": ["console", "file"],
      "propagate": true
    },
    "main_logging_config": {
      "level": "DEBUG",
      "handlers": ["console", "file"],
      "propagate": false
    }
  }
}
  • disable_existing_loggers: 이미 존재하는 로거들을 비활성화시킬지 여부. true이면 logger.json의 설정을 우선 사용한다는 의미.
  • propagate : 부모 Logger가 자식 Logger에게 해당 로그를 전파시킬것인지 여부. true로 하면 동일로그가 중복으로 발생하는 문제가 생길 수 있다. false로 하면 중복 발생하지 않는다.

yaml 파일로 관리

json 파일과 동일하게 yaml로 관리할 수 있다. 아래와 같이 yml 파일을 읽어들인다.

with open("./logger.yml", 'rt') as f:
    loggingConfig = yaml.safe_load(f.read())
    logging.config.dictConfig(loggingConfig)


# 로거 생성
logger = logging.getLogger(__name__)

그리고 logger.yml 파일을 생성한다.

version: 1
disable_existing_loggers: False
formatters:
  simple:
    format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    datefmt: "%Y-%m-%d %H:%M:%S"
handlers:
  console_handler:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
  file_handler:
    class: logging.FileHandler
    level: DEBUG
    formatter: simple
    filename: app.log
root:
  level: WARN
  handlers: [console_handler, file_handler]

loggers:
  main_logging_config:
    level: DEBUG
    handlers: [console_handler, file_handler]
    propagate: false

참고

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유