728x90
반응형
1. 로깅 시작하기 및 기본 레벨 이해하기
1.1 5가지 로깅 레벨
- logging 모듈은 이벤트의 심각도(Severity)를 나타내는 다섯 가지의 로그 레벨을 제공
|
레벨 이름
|
심각도
|
설명
|
|
DEBUG
|
가장 낮음
|
상세 정보, 개발 시 주로 사용
|
|
INFO
|
보통
|
예상대로 작동하는 것에 대한 확인
|
|
WARNING
|
보통
|
예상치 못한 상황 발생, 하지만 소프트웨어는 정상 작동 중 (기본 출력 레벨)
|
|
ERROR
|
높음
|
심각한 문제로 인해 일부 기능이 수행될 수 없음
|
|
CRITICAL
|
가장 높음
|
프로그램 자체가 계속 실행될 수 없는 심각한 오류
|
1.2 기본 동작 확인
import logging
logging.debug("디버그 메시지 - 출력되지 않음")
logging.info("정보 메시지 - 출력되지 않음")
logging.warning("경고 메시지 - 출력됨")
logging.error("오류 메시지 - 출력됨")
logging.critical("치명적 오류 메시지 - 출력됨")
2. 로깅의 기본 구성 변경하기 (basicConfig)
- 기본 설정은 warning 레벨부터 로깅하고, 출력 포맷이 단순
- 만약 debug 메시지까지 보고 싶거나 출력 형식을 변경하고 싶다면 logging.basicConfig() 함수를 사용해야 합니다.
- 이 설정은 일반적으로 logging 모듈을 가져온 직후에 설정하는 것이 가장 효율적
- basicConfig를 통해 레벨(level), 형식(format), 날짜 형식(date format) 등의 인수를 지정할 수 있습니다.
2.1 로깅 레벨 변경
- 로깅 레벨을 logging.debug로 설정하면 모든 메시지(DEBUG, INFO, WARNING, ERROR, CRITICAL)가 출력됩니다.
2.2 출력 형식 사용자 지정
- format 인수에 문자열을 전달하여 출력 형식을 상세하게 지정 가능
- 이 문자열 안에는 다음과 같은 로그 레코드 속성(Lock Record Attributes)을 포함 가능
- %(name)s: 로거 이름
- %(asctime)s: 시간
- %(levelname)s: 레벨 이름
- %(message)s: 실제 메시지
- 또한 datefmt 인수를 사용하여 시간 형식을 지정할 수 있습니다 (예: %m은 월, %d는 일, %H는 시).
import logging
import time
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y/%m/%d %H:%M:%S'
)
logging.debug("이제 디버그 메시지가 보여요!")
logging.info("정보 메시지 예시")
logging.warning("경고 메시지 예시")
- 기본적으로 로거 이름(%(name)s)을 지정하지 않으면 'root' 로거가 사용됩니다.
3. 여러 모듈에서 로깅 사용하기 (로거 계층 구조)
- 애플리케이션이 여러 모듈로 구성될 때, 모든 곳에서 'root' 로거를 사용하는 것은 좋지 않습니다.
- 각 모듈은 자신만의 로거를 생성하는 것이 모범 사례
3.1 모듈별 로거 생성
- 각 모듈에서 로거를 생성하려면 logging.getLogger()를 사용하고, 로거 이름으로 __name__ 전역 변수를 사용하는 것이 좋습니다:
# helper.py
import logging
logger = logging.getLogger(__name__) # 예: helper
def do_something():
logger.debug("헬퍼 디버그")
logger.info("헬퍼 정보")
# app.py
import logging
from helper import do_something
logging.basicConfig(level=logging.INFO, format='%(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__) # 예: app
logger.info("앱 시작")
do_something()
- 이렇게 생성된 로거는 루트 로거에서 시작하는 계층 구조에 추가
3.2 로깅 전파 제어
- 새로운 로거는 메시지를 기본(루트) 로거로 전파합니다 (propagate의 기본값은 True).
- 만약 이 전파를 막고 싶다면 다음과 같이 설정할 수 있습니다:
import logging
logger = logging.getLogger("myapp.module")
logger.setLevel(logging.DEBUG)
logger.propagate = False # 루트로 전파하지 않음
# 직접 핸들러를 붙임
stream_h = logging.StreamHandler()
stream_h.setLevel(logging.DEBUG)
stream_h.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s'))
logger.addHandler(stream_h)
logger.info("이 메시지는 module 로거에서만 처리되고 루트로 안감")
- 전파를 비활성화하면, 기본 로거에는 메시지가 전달되지 않음
4. 고급 구성 — 핸들러 (Handler) 사용하기
- 핸들러 객체는 로그 메시지를 실제 대상(파일, 스트림, 이메일 등)으로 보내는 역할을 담당
- 하나의 로거에 여러 개의 핸들러를 붙여서, 로그 메시지를 여러 대상으로 동시에 보내는 것이 가능
4.1 핸들러 설정 및 로거에 추가
- 예를 들어, warning 메시지는 콘솔(스트림)에, error 메시지는 파일에만 로깅하고 싶다면 핸들러를 사용합니다:
- 1. 로거 생성: logging.getLogger(__name__)을 사용
- 2. 핸들러 생성: StreamHandler와 FileHandler를 생성
- 3. 핸들러별 레벨/포맷 설정: 각 핸들러에 대해 setlevel() 및 setFormatter()를 호출
- 4. 로거에 핸들러 추가: logger.addHandler()를 사용하여 핸들러를 로거에 추가
import logging
logger = logging.getLogger("example")
logger.setLevel(logging.DEBUG)
logger.propagate = False
# 콘솔 핸들러는 WARNING 이상만 출력
stream_h = logging.StreamHandler()
stream_h.setLevel(logging.WARNING)
stream_h.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s'))
# 파일 핸들러는 ERROR 이상만 로그 파일에 기록
file_h = logging.FileHandler('error.log', encoding='utf-8')
file_h.setLevel(logging.ERROR)
file_h.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger.addHandler(stream_h)
logger.addHandler(file_h)
logger.info("정보 - 어디에도 출력되지 않음")
logger.warning("경고 - 콘솔에 출력")
logger.error("오류 - 콘솔과 error.log에 기록")
- 콘솔: WARNING, ERROR 메시지 표시
- 파일 error.log : ERROR 메시지에 대한 타임스탬프 포함 로그 행 존재
5. 문제 해결을 위한 스택 추적 캡처하기
- 예외(Exception) 발생 시 스택 추적(Stack Trace) 정보를 로깅하는 것은 문제 해결에 매우 유용
5.1 exc_info=True 사용
- 예외를 try-except 블록으로 잡을 때, logging.error() 메서드에 exc_info=True 인수를 설정하면 스택 추적이 로그에 포함
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
try:
my_list = [16, 18] # 올바른 리스트 초기화
value = my_list[19] # IndexError 발생
except IndexError as e:
logging.error("인덱스 오류 발생 (exc_info 포함)", exc_info=True)
6. 대규모 애플리케이션을 위한 파일 로테이션 핸들러
- 로그 메시지가 많은 대규모 애플리케이션의 경우, 로그 파일 크기가 무한정 커지는 것을 방지해야 합니다.
- 이럴 때 **로테이션 파일 핸들러(Rotating File Handler)**를 사용합니다.
6.1 크기 기반 로테이션 (RotatingFileHandler)
- RotatingFileHandler를 사용하면 최대 바이트(maxBytes)를 지정하여 파일 크기가 이 한도를 초과하면 새 로그 파일로 롤오버(rollover)
- backupCount를 지정하여 보관할 백업 파일의 개수를 제한
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("rotating_example")
logger.setLevel(logging.DEBUG)
handler = RotatingFileHandler('app.log', maxBytes=1024, backupCount=3, encoding='utf-8')
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
# 테스트: 많은 메시지를 찍어 파일이 롤오버되도록 함
for i in range(1000):
logger.debug(f"디버그 메시지 {i}")
6.2 시간 기반 로테이션 (TimedRotatingFileHandler)
- 애플리케이션이 장기간 실행되는 경우, 시간 간격(when)을 기준으로 로그 파일을 로테이션
- when 인수에 초(S), 분(M), 시(H), 일(D), 자정(midnight) 또는 요일(W0: 월요일, W1: 화요일 등)을 지정
import logging
from logging.handlers import TimedRotatingFileHandler
import time
logger = logging.getLogger("timed_example")
logger.setLevel(logging.INFO)
handler = TimedRotatingFileHandler('time_app.log', when='S', interval=5, backupCount=3, encoding='utf-8')
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
# 테스트: 몇 초마다 로그를 찍어 파일이 변경되는지 확인
for i in range(20):
logger.info(f"타임 로테이트 테스트 {i}")
time.sleep(1)
7. 외부 설정 파일 사용하기
- 로깅 구성을 코드 내부에 하드 코딩하는 대신, 별도의 파일(logging.conf, logging.ini)을 사용하여 로거, 핸들러, 포매터를 정의 가능
- 이러한 외부 구성을 사용하려면 logging.config 모듈을 가져와 fileConfig 또는 dictConfig 메서드를 사용
# dictConfig 예시
import logging
import logging.config
LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"standard": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "WARNING",
"formatter": "standard",
"stream": "ext://sys.stdout"
},
"errors_file": {
"class": "logging.FileHandler",
"level": "ERROR",
"formatter": "standard",
"filename": "dict_error.log",
"encoding": "utf-8"
}
},
"loggers": {
"": { # root logger
"handlers": ["console", "errors_file"],
"level": "DEBUG",
"propagate": False
},
"myapp.special": {
"handlers": ["console"],
"level": "INFO",
"propagate": False
}
}
}
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("myapp.special")
logger.info("myapp.special의 정보(콘솔에만)")
logger.error("myapp.special의 오류(콘솔 + dict_error.log에 기록)")
# fileconfig
import logging
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('simpleExample') # conf에서 정의된 로거 이름
logger.error("fileConfig로 로깅 테스트")
https://github.com/JK-17/Python_Logging_Prac.git
GitHub - JK-17/Python_Logging_Prac
Contribute to JK-17/Python_Logging_Prac development by creating an account on GitHub.
github.com
*** 위 링크에서 확인 가능 ***
728x90
반응형
'프로그래밍 > Python' 카테고리의 다른 글
| [파이썬 / 기본] Random 라이브러리 (0) | 2025.12.01 |
|---|---|
| [파이썬 / 기본] Errors and Exceptions (오류와 예외처리) (0) | 2025.10.23 |
| [파이썬 / 기본] Lambda Function (0) | 2025.10.22 |
| [파이썬 / 기본] itertools (0) | 2025.10.21 |
| [파이썬 / 기본] collections (0) | 2025.10.20 |