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

print 는 프로토타입용. logging 은 살아있는 코드용. logging 이 레벨 (DEBUG/INFO/WARNING/ERROR/CRITICAL), 핸들러 (어디 쓸지), 포매터 (어떻게 보일지), 코드 안 만지고 켜기/끄기 능력 줘. 일주일 쓰면 print 로 돌아가는 게 원시적 느낌.

5 레벨

DEBUG — 개발용 verbose 추적. INFO — 정상 운영 마크 이벤트. WARNING — 비정상이지만 실패 X. ERROR — 프로그램 안 죽인 실패. CRITICAL — 죽기 직전. 로거에 레벨 설정하면 그 아래 다 필터 — INFO 설정이 DEBUG 떨어뜨림.

라이브러리에선 logger.info(), logging.info() X

표준 라이브러리 패턴 — 각 모듈 상단에 logger = logging.getLogger(__name__). logger.info(...) 사용, logging.info(...) 절대 X. named logger 가 애플리케이션이 모듈별 verbosity 설정 가능 — 한 모듈 noisy debug, 다른 거 조용히 원할 때 유용. __name__ 이 import path 자동.

basicConfig — 스크립트용

로그 stderr 또는 파일로 가야 할 스크립트엔 logging.basicConfig(level=INFO, format='...') 가 한 줄. 큰 애플리케이션엔 핸들러 + 포매터 명시적 설정 — 다음 코드 블록.

원칙: 라이브러리는 logger.info("...") 호출. 애플리케이션이 상단에서 한 번 설정 — 어떤 레벨, 어떤 포맷, 어디 쓸지. 라이브러리에서 basicConfig 호출 X — 애플리케이션이 결정하게.

Code

Quick start — 스크립트용 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__: 비상!
라이브러리 패턴 — getLogger(__name__)·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
예외 로깅 — exc_info / exception()·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
포맷 문자열 — lazy 포맷·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.pydef play(level: str) 정의 (5 레벨 모두에서 로그). logging.getLogger(__name__) 사용. 별 스크립트에서 basicConfig 로 INFO 레벨 설정, 그 다음 quester 로거 레벨을 명시적으로 DEBUG 로 설정 — 그 DEBUG 메시지도 나타나도록. 둘 다 관찰 — (a) 다른 모듈 DEBUG 침묵, (b) quester DEBUG 표시.

Progress

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

댓글 0

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

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