안녕하세요! 그루비한 입니다. 이번 포스트는 파이썬에 로그를 활용하는 방법입니다.
파이썬 파일을 10시간 정도 실행시켰는데, 뭔가 코드의 오류로 실행 결과가 저장되지 않는다면 정말 절망적일 수 밖에 없습니다. (실제 경험담..)
그래서 저는 이런 불안감을 잠재우고자 파이썬의 logging을 활용해보기로 하였습니다. print를 사용하는 것보다 터미널에 실행결과가 출력되지는 않지만 로그 파일에 현재 실행되고 있는 상태가 저장이 되어 좀 더 효율적이었습니다.
이렇게 로그를 남겨두는 것은 추후에 파이썬 파일의 실행 경과 (시간, 결과) 등을 확인할 때도 용이하고, 에러 발생 로그를 남겨둘 때도 많이 활용되기 때문에 이번 시간에 간단한 로깅 활용법을 정리해보겠습니다.
Logging(로깅) 은 프로그램 실행 중 발생하는 이벤트, 오류, 상태 변화 등을 기록하는 과정입니다. 이를 통해 프로그램이 어떻게 동작하는지 파악하고, 예상치 못한 버그나 성능 문제를 추적할 수 있습니다.
언제 Logging(로깅)을 활용할까?
📌 디버깅 & 문제 해결: 코드가 예상과 다르게 동작할 때
📌 운영 환경 모니터링: 서버 애플리케이션의 상태를 기록할 때
📌 에러 추적: 예외 발생 시 원인을 빠르게 파악할 때
📌 사용자 행동 분석: 웹 애플리케이션에서 사용자의 요청을 기록할 때
로그를 기록하기 위해서 파이썬에서는 logging모듈을 제공하고 있습니다.logging 모듈의 Logger, Handler, Formatter 개념을 활용하면 더욱 강력하게 로그를 관리할 수 있습니다.
아래는 기본 예시 입니다.
import logging
# 로그 포맷 설정
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")
# 콘솔 핸들러 생성 및 포맷 적용
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
# 로거 생성 및 핸들러 추가
logger = logging.getLogger("example_logger")
logger.setLevel(logging.INFO)
logger.addHandler(console_handler)
# 로그 출력
logger.info("로그 메시지")
self.logger = logging.getLogger(self.logname)
self.logger.setLevel(logging.INFO)
getLogger: 로그를 생성합니다.set level: 로그의 유형을 선택합니다.로그의 유형
사용자가 원하는 로그의 유형을 선택합니다. 로그 저장 시에 어떤 유형의 로그인지 함께 출력됩니다.
| 유형 | 설명 |
|---|---|
| DEBUG | 개발 중에 유용한 상세한 정보 |
| INFO | 일반적인 정보 메시지 |
| WARNING | 경고 메시지 (잠재적 문제) |
| ERROR | 오류 메시지 |
| CRITICAL | 치명적인 오류 |
로그의 출력 위치를 결정합니다. 핸들러에 따라 로그를 콘솔에 출력할지, 파일에 저장할지 등을 선택할 수 있습니다.
file_handler = logging.FileHandler(self.dir): 해당 위치에 .log 파일 생성 및 로그 출력logger.addHandler(file_handler): 정의한 로그에 핸들러를 추가합니다.핸들러의 종류
| 핸들러 | 설명 |
|---|---|
StreamHandler | 콘솔(터미널)에 로그 출력 |
FileHandler | 로그를 파일에 저장 |
RotatingFileHandler | 일정 크기가 되면 새 파일 생성 (로그 파일 관리에 유용) |
TimedRotatingFileHandler | 일정 시간이 지나면 새 파일 생성 (예: 매일 새로운 로그 파일 생성) |
SMTPHandler | 이메일로 로그 전송 |
HTTPHandler | 웹 서버로 로그 전송 |
SocketHandler | 네트워크 소켓을 통해 로그 전송 |
로그 메세지의 출력 형식(포맷)을 정의합니다. 핸들러를 통해 출력될 로그에 시간, 파일명, 메세지 등을 포함할 수 있도록 정의합니다.
예시
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
출력 예시
2025-03-27 14:30:45,678 - my_logger - INFO - 로그 메시지 출력
주요 포맷 코드
| 포맷 코드 | 설명 |
|---|---|
%(asctime)s | 로그 발생 시간 |
%(name)s | 로거 이름 |
%(levelname)s | 로그 레벨 (DEBUG, INFO, WARNING...) |
%(message)s | 로그 메시지 |
%(filename)s | 실행 중인 파일 이름 |
%(lineno)d | 로그가 발생한 코드의 줄 번호 |
%(funcName)s | 로그가 발생한 함수 이름 |
포매터(Formatter)에서 정의한 대로 로그를 출력할 때, 로그 메세지를 입력해줘야 합니다. 이때 각 로그의 유형에 맞춰서 메세지만 입력해주면 되고, 다른 포맷 정보들은 자동으로 입력됩니다.
예시
logger.debug("디버깅 메시지")
logger.info("정보 메시지")
logger.warning("경고 메시지")
logger.error("에러 메시지")
logger.critical("치명적인 오류")
출력 예시
logger.info()에 대한 출력 예시입니다.
2025-03-27 15:00:00,123 - INFO - example_logger - 로그 메시지
제가 실제로 로깅을 활용하기 위해 구현한 클래스입니다. 코드는 해피코더님의 블로그를 참고하였습니다.
import logging
class logSave():
def __init__(self,dir,logname):
self.logname = logname #로그 이름
self.dir = os.path.join(dir,logname) #로그 파일 저장 위치
self.InitLogger() #로그 정의 함수
#로그를 정의합니다.
def InitLogger(self):
#로거 생성
self.logger = logging.getLogger(self.logname)
self.logger.setLevel(logging.INFO)
#핸들러 추가
file_handler = logging.FileHandler(self.dir)
#포맷 설정
formatter = logging.Formatter("[%(asctime)s] %(message)s")
file_handler.setFormatter(formatter)
#핸들러를 로거에 추가
self.logger.addHandler(file_handler)
#기타
self.logger.propagate = False #터미널에서 출력되지 않도록 설정
#로그 기록
def LogTextOut(self, msg):
self.logger.info(str(msg))
if __name__ == "__main__":
log = logSave("./new_real_data","saving.log")
#(생략)
log.LogTextOut(f"피쳐 추출 현황 {str(len(new_data))}")
이 코드를 활용하여 저는 피쳐가 잘 추출되고 있는지 로그를 통해 확인할 수 있었습니다. 이제 파이썬 파일이 잘 동작하는지 print()함수가 아닌 로깅(logging)을 써보면 어떨까요?