본문 바로가기
C.W.K.
Stream
Lesson 05 of 06 · published

logging — 나중에 사건을 재구성할 기록

~18 min · logging, logger, handler, formatter

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

print와 다른 점은 통제 가능성

logging은 DEBUG·INFO·WARNING·ERROR·CRITICAL 수준, 출력 위치를 정하는 핸들러, 모양을 정하는 포매터를 분리해 코드 변경 없이 무엇을 남길지 고를 수 있어.

라이브러리와 애플리케이션의 책임을 나눠

모듈은 logging.getLogger(__name__)로 자기 logger를 만들고 메시지만 남겨. 수준·형식·파일 같은 설정은 최상위 애플리케이션이 한 번 정하며, 라이브러리는 basicConfig를 호출하지 않아.

운영 로그는 사건의 필드를 남겨

요청 식별자, 대상, 결과, 걸린 시간처럼 원인을 되짚을 값을 기록하고, 예외는 logger.exception으로 traceback과 함께 남겨. 포맷 인자는 지연 평가되는 %s 방식으로 넘겨.

Code

스크립트의 basicConfig 시작점·python
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)

logger = logging.getLogger(__name__)

logger.debug("상세 정보 — DEBUG 레벨에만 표시")
logger.info("정상 운영 이벤트")
logger.warning("뭔가 어긋남")
logger.error("실패 발생")
logger.critical("비상!")

# 출력 (level=INFO 가 DEBUG 떨어뜨림):
# 2026-05-02 ... [INFO] __main__: 정상 운영 이벤트
# 2026-05-02 ... [WARNING] __main__: 뭔가 어긋남
# 2026-05-02 ... [ERROR] __main__: 실패 발생
# 2026-05-02 ... [CRITICAL] __main__: 비상!
모듈별 logger 만들기·python
# my_library.py
import logging

logger = logging.getLogger(__name__)        # 모듈 이름

def do_work(item):
    logger.debug("%r 작업 시작", item)
    try:
        result = item.upper()
    except AttributeError:
        logger.error("%r 가 문자열 아님", item)
        raise
    logger.info("%r 처리됨", item)
    return result

# 애플리케이션에서:
# import logging
# logging.basicConfig(level=logging.INFO)
# logging.getLogger('my_library').setLevel(logging.DEBUG)   # 이 모듈만 noisy
예외와 traceback 함께 기록하기·python
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def divide(a, b):
    try:
        return a / b
    except Exception:
        # logger.exception 이 ERROR 레벨 + traceback 으로 로그 — 등가물
        # logger.error("...", exc_info=True)
        logger.exception("divide(%s, %s) 실패", a, b)
        return None

result = divide(10, 0)
# ERROR ... divide(10, 0) 실패
# Traceback (most recent call last):
#   ...
# ZeroDivisionError: division by zero
필요할 때만 계산하는 로그 포맷·python
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

user = "alice"
count = 42

# 안 좋음 — DEBUG 레벨에서도 포맷 (필터되면 낭비)
logger.debug(f"user {user} has count {count}")

# 좋음 — % 포맷, 레벨 활성화돼야 포맷
logger.debug("user %s has count %d", user, count)

# info/warning/error 도 같음
logger.info("connected to %s on port %d", "localhost", 8000)
stderr와 파일에 서로 다른 수준 남기기·python
import logging

logger = logging.getLogger("my_app")
logger.setLevel(logging.DEBUG)

# 핸들러 1 — INFO 의 stderr
stream = logging.StreamHandler()
stream.setLevel(logging.INFO)
stream.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
logger.addHandler(stream)

# 핸들러 2 — DEBUG 의 파일 (전부)
file_h = logging.FileHandler("/tmp/app.log", mode="a")
file_h.setLevel(logging.DEBUG)
file_h.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
logger.addHandler(file_h)

logger.debug("상세 — 파일에만")
logger.info("중요 — stderr 와 파일 둘 다")

External links

Exercise

다섯 수준에서 기록하는 quester.play 모듈을 만들고 getLogger(__name__)을 써. 별도 스크립트에서 전체는 INFO, quester만 DEBUG로 설정해 다른 모듈의 DEBUG는 숨고 quester의 DEBUG는 보이는지 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.