파이썬 logging 모듈 완전 정복 📜 — 콘솔·파일·회전 로그까지
파이썬 logging 모듈 완전 정복 📜
운영 서비스에서는 print보다 logging이 정답입니다.
레벨 제어, 포맷팅, 파일 저장, 로테이팅(회전), 비동기 처리까지 지원해
개발·테스트·프로덕션 어디서든 같은 인터페이스로 로그를 다룰 수 있습니다.
1) 로그 레벨과 가장 쉬운 사용법
import logging
logging.basicConfig(level=logging.INFO)
logging.debug("디버그") # 표시 안 됨(레벨 INFO 이상만)
logging.info("정보 로그입니다")
logging.warning("경고!")
logging.error("에러 발생")
logging.critical("치명적 오류")
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger("myapp")
logger.info("포맷이 적용된 로그")
3) 파일 로그 & 로테이팅
3-1. 파일로 기록
import logging
file_handler = logging.FileHandler("app.log", encoding="utf-8")
file_handler.setFormatter(logging.Formatter(
"%(asctime)s %(levelname)s [%(name)s] %(message)s"))
logger = logging.getLogger("myapp")
logger.setLevel(logging.INFO)
logger.addHandler(file_handler)
logger.info("파일에 적재됩니다")
3-2. 크기 기준 회전: RotatingFileHandler
import logging
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
"app_size.log",
maxBytes=5 * 1024 * 1024, # 5MB
backupCount=5 # 최대 백업 파일 수
)
handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
logger = logging.getLogger("rotate.size")
logger.setLevel(logging.INFO)
logger.addHandler(handler)
logger.info("크기 기준 회전 설정 완료")
3-3. 시간 기준 회전: TimedRotatingFileHandler
import logging
from logging.handlers import TimedRotatingFileHandler
handler = TimedRotatingFileHandler(
"app_time.log",
when="midnight", # 'S','M','H','D','W0'~'W6','midnight'
interval=1,
backupCount=7,
encoding="utf-8"
)
handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
logger = logging.getLogger("rotate.time")
logger.setLevel(logging.INFO)
logger.addHandler(handler)
logger.info("시간 기준 회전 설정 완료")
4) 프로젝트급 설정: dictConfig
import logging
import logging.config
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"std": {
"format": "%(asctime)s %(levelname)s [%(name)s] %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "std"
},
"file": {
"class": "logging.handlers.TimedRotatingFileHandler",
"level": "INFO",
"formatter": "std",
"filename": "service.log",
"when": "midnight",
"backupCount": 14,
"encoding": "utf-8"
}
},
"loggers": {
"": { # root
"level": "INFO",
"handlers": ["console", "file"]
},
"myapp.db": { # 모듈별 오버라이드
"level": "WARNING",
"propagate": True
}
}
}
logging.config.dictConfig(LOGGING)
logger = logging.getLogger("myapp")
logger.info("dictConfig로 중앙 관리!")
5) JSON 로깅 (ELK/CloudWatch 대비)
import json, logging, time
class JSONFormatter(logging.Formatter):
def format(self, record):
payload = {
"ts": time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(record.created)),
"level": record.levelname,
"logger": record.name,
"msg": record.getMessage()
}
if record.exc_info:
payload["exc_info"] = self.formatException(record.exc_info)
return json.dumps(payload, ensure_ascii=False)
logger = logging.getLogger("json")
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.info({"event": "user_login", "user_id": 123})
6) 고부하 대비: QueueHandler로 비동기
import logging, queue, threading
from logging.handlers import QueueHandler, QueueListener
log_q = queue.Queue(maxsize=10000)
queue_handler = QueueHandler(log_q)
file_handler = logging.FileHandler("async.log", encoding="utf-8")
listener = QueueListener(log_q, file_handler)
listener.start()
logger = logging.getLogger("async")
logger.setLevel(logging.INFO)
logger.addHandler(queue_handler)
logger.info("메인 스레드를 막지 않고 기록합니다")
7) 베스트 프랙티스 & 주의사항
- print 금지: 운영·스케줄러·서버 작업은
logging만 사용.
- 레벨 규칙: 개발(디버그↑), 운영(정보↑), 타사 라이브러리 노이즈는 모듈별 레벨로 제어.
- 로그 로테이션 필수: 크기 또는 시간 기준 중 하나는 반드시 설정.
- PII/비밀키 마스킹: 포맷터 또는 필터로 민감정보 제거.
- 멀티프로세스:
QueueHandler + 중앙 리스너 프로세스로 수집 권장.
- 컨테이너: 파일 대신 stdout 로깅 + 런타임 수집(예: Docker, k8s)이 관리에 유리.
요약
logging은 운영 필수 기능(레벨·포맷·핸들러·회전)을 제공
dictConfig로 프로젝트 전역 설정을 깔끔하게 관리
- 대용량/분산 환경은 JSON 로깅과 QueueHandler로 확장
자주 묻는 질문(FAQ)
Q1. 모듈마다 로그 레벨을 다르게 하고 싶어요.
A. logging.getLogger("패키지.모듈")별로 레벨을 설정하거나 dictConfig.loggers에서 개별 지정하세요.
Q2. 포매터에서 요청 ID 같은 필드를 넣으려면?
A. LoggerAdapter 또는 Filter로 extra 컨텍스트를 주입해 포맷 문자열에서 %(request_id)s처럼 사용하세요.
댓글
댓글 쓰기