소프트웨어 개발에서 로깅은 애플리케이션의 동작 상태를 추적하고 디버깅하는 데 필수적입니다. Python의 logging 모듈은 유연하고 강력한 로깅 시스템을 제공하며, 다양한 출력 대상과 형식을 지원합니다. 이 모듈은 다섯 가지 기본 로그 레벨을 제공합니다: debug(), info(), warning(), error(), critical().
1. 기본 사용법
가장 간단한 형태로 logging.basicConfig()를 사용하여 로깅을 설정할 수 있습니다. 이 함수는 한 번만 호출되며, 이후의 모든 로깅 호출에 영향을 줍니다.
import logging
logging.basicConfig(
format='%(asctime)s %(levelname)s %(message)s %(name)s',
datefmt='%m-%d-%Y %I:%M:%S %p',
filename='app.log',
level=logging.INFO
)
logging.debug("디버그 메시지")
logging.info("정보 메시지")
logging.warning("경고 메시지")
2. 로그 레벨 이해하기
로그 레벨은 중요도에 따라 다음과 같이 정렬됩니다: CRITICAL > ERROR > WARNING > INFO > DEBUG. 기본적으로 root 로거의 레벨은 WARNING으로 설정되어 있어, WARNING 이상의 메시지만 출력됩니다.
import logging
logging.debug("디버그")
logging.info("서비스 시작")
logging.warning("버전 만료 예정")
logging.error("404 오류")
logging.critical("시스템 다운")
이 코드를 실행하면 warning, error, critical 메시지만 출력됩니다. 모든 레벨을 출력하려면 레벨을 NOTSET으로 설정해야 합니다.
import logging
logging.basicConfig(level=logging.NOTSET)
logging.debug("디버그")
logging.info("서비스 시작")
3. 파일에 로그 저장하기
3.1 기본 파일 핸들러
FileHandler를 사용하여 로그를 파일에 기록할 수 있습니다.
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.FileHandler("application.log")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info("애플리케이션 시작")
logger.warning("리소스 부족")
logger.info("작업 완료")
3.2 콘솔과 파일에 동시 출력
StreamHandler를 추가하면 로그를 콘솔과 파일에 동시에 출력할 수 있습니다.
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
file_handler = logging.FileHandler("app.log")
file_handler.setLevel(logging.INFO)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
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.info("정보 메시지")
3.3 로그 로테이션
RotatingFileHandler를 사용하면 로그 파일이 일정 크기를 넘었을 때 자동으로 백업 파일을 생성할 수 있습니다.
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
rotating_handler = RotatingFileHandler(
"app.log",
maxBytes=1024, # 1KB
backupCount=3 # 최대 3개의 백업 파일
)
rotating_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
rotating_handler.setFormatter(formatter)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
logger.addHandler(rotating_handler)
logger.addHandler(console_handler)
logger.info("로테이션 테스트")
3.4 예외 추적 기록
traceback 정보를 로그에 포함시키려면 exc_info 파라미터를 True로 설정합니다.
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.FileHandler("error.log")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
logger.addHandler(handler)
logger.addHandler(console_handler)
try:
open("config.txt", "rb")
except Exception:
logger.error("파일 열기 실패", exc_info=True)
3.5 다중 모듈에서 로깅 사용하기
로깅 설정은 상속 구조를 통해 여러 모듈에서 공유할 수 있습니다.
main_module.py
import logging
import sub_module
logger = logging.getLogger("mainModule")
logger.setLevel(logging.INFO)
handler = logging.FileHandler("multi.log")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - [%(levelname)s] - %(message)s')
handler.setFormatter(formatter)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
logger.addHandler(handler)
logger.addHandler(console_handler)
logger.info("sub_module 인스턴스 생성")
obj = sub_module.SubModuleClass()
obj.do_something()
sub_module.py
import logging
module_logger = logging.getLogger("mainModule.sub")
class SubModuleClass:
def __init__(self):
self.logger = logging.getLogger("mainModule.sub.module")
self.logger.info("SubModuleClass 인스턴스 생성")
def do_something(self):
self.logger.info("작업 수행 중")
4. 설정 파일을 통한 로깅 구성
logging.conf 파일을 사용하면 코드와 설정을 분리할 수 있어 유지보수가 용이합니다.
logging.conf
[loggers]
keys=root,simpleExample
[handlers]
keys=fileHandler,consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=fileHandler
[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=1
[handler_consoleHandler]
class=StreamHandler
args=(sys.stdout,)
level=DEBUG
formatter=simpleFormatter
[handler_fileHandler]
class=FileHandler
args=("logging.log", "a")
level=DEBUG
formatter=simpleFormatter
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
Python 코드
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger("simpleExample")
logger.debug("디버그 메시지")
logger.info("정보 메시지")
5. JSON 또는 YAML 파일을 통한 로깅 구성
Python 3.2부터는 dictConfig()를 통해 딕셔너리 형태로 로깅 설정을 전달할 수 있습니다. 이를 활용하여 JSON, YAML 파일을 로깅 설정에 사용할 수 있습니다.
logging.yaml
version: 1
formatters:
simple:
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: simple
stream: ext://sys.stdout
file:
class: logging.handlers.RotatingFileHandler
level: DEBUG
formatter: simple
filename: app.log
maxBytes: 1024
backupCount: 3
loggers:
simpleExample:
level: DEBUG
handlers: [console]
propagate: no
root:
level: DEBUG
handlers: [file]
Python 코드
import logging.config
import yaml
with open("logging.yaml", "r") as config_file:
config_dict = yaml.safe_load(config_file)
logging.config.dictConfig(config_dict)
logger = logging.getLogger("simpleExample")
logger.debug("YAML 설정 테스트")
logger.info("정보 메시지")
dictConfig() 함수는 딕셔너리의 version 키를 필수로 요구하며, 나머지 키는 선택 사항입니다. 외부 객체(sys.stdout 등)를 참조해야 할 경우 ext:// 접두사를 사용합니다.