본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.
파이썬 💻: 프로그램의 숨은 이야기꾼, Logging으로 디버깅 마스터하기! 🕵️♂️

안녕하세요, 여러분! 😊
지난 시간에는 input()과 print(), 그리고 파일 처리(open(), with 문)를 통해 프로그램이 세상과 소통하고 중요한 정보를 영구적으로 기억하는 방법을 배웠습니다. 💾🗣️ 덕분에 우리 프로그램은 사용자와 대화하고, 데이터를 파일에 차곡차곡 저장하며 더욱 똑똑해졌죠! 마치 프로그램에 일기장을 만들어준 것 같아요. 📖 이제 여러분의 프로그램은 단순한 계산기를 넘어, 사용자의 입력을 기억하고 결과를 파일로 남길 수 있는 어엿한 정보 관리 도구가 되었을 겁니다.
자, 이렇게 사용자와 소통하고 데이터를 기억하는 프로그램이 실행되는 동안 어떤 일들이 벌어지는지, 예상치 못한 문제가 발생했을 때 그 원인을 어떻게 추적할 수 있을까요? 🤔 바로 '로깅(Logging)'이 그 해답을 줄 수 있습니다! 마치 비행기의 블랙박스처럼, 로깅은 프로그램의 실행 상태, 오류, 중요한 이벤트들을 기록하여 문제가 발생했을 때 원인을 파악하고, 시스템의 동작을 이해하는 데 결정적인 단서를 제공합니다. 🔍 개발자에게는 정말 든든한 탐정 친구 같은 존재죠!
그래서 오늘은 파이썬의 강력한 logging 모듈을 통해 프로그램의 발자취를 기록하고, 문제를 효과적으로 진단하는 방법을 배울 거예요! 파이썬 기본 과정의 마지막을 장식할 로깅의 세계로 함께 떠나봅시다! 🚀
오늘 함께 알아볼 내용은 다음과 같습니다:
- Logging, 왜 중요할까요? 📢 (The importance of logging)
- 파이썬 logging 모듈 소개 🛠️ (Introduction to the logging module)
- 기본 로깅 설정과 출력 레벨 ⚙️ (Basic configuration and output levels)
- 로그 메시지, 보기 좋게 꾸미기: 출력 결과 Formatting 🎨 (Formatting log messages)
- 오류의 순간 포착! Stack Traces 캡처하기 🔍 (Capturing Stack Traces)
- logging 모듈의 핵심 구성 요소: 클래스와 함수들 🧩 (Key classes and functions)
- 로그를 어디에나! 다양한 Handler 사용하기 📬 (Using various Handlers)
- 더욱 정교한 제어: Other configurations (fileConfig, dictConfig) 🗂️ (Advanced configurations)
프로그램의 신뢰성을 한층 높여줄 로깅 기술, 배울 준비 되셨나요? 함께 시작해봅시다! ✨

Logging, 왜 중요할까요? 📢
우리가 만든 프로그램이 복잡해질수록, 예상치 못한 오류가 발생할 가능성도 커집니다. print() 문으로 중간중간 값을 확인하는 것도 한계가 있죠. 로깅은 이런 상황에서 다음과 같은 중요한 역할을 합니다:
- 디버깅: 오류 발생 시점, 오류 내용, 당시의 주요 변수 값 등을 기록하여 원인 분석을 용이하게 합니다.
- 모니터링: 프로그램이 정상적으로 동작하는지, 특정 기능이 예상대로 수행되는지 등을 실시간 또는 주기적으로 확인할 수 있습니다.
- 감사 추적 (Audit Trail): 중요한 작업(예: 데이터 변경, 사용자 로그인)이 언제, 누구에 의해, 어떻게 수행되었는지 기록으로 남길 수 있습니다.
- 성능 분석: 특정 작업에 소요되는 시간을 기록하여 병목 지점을 찾는 데 활용할 수 있습니다.
간단히 말해, 로깅은 프로그램의 "실행 일지"를 작성하는 것과 같습니다. 잘 작성된 로그는 문제 해결의 시간을 극적으로 단축시켜주고, 프로그램의 안정성을 높이는 데 크게 기여합니다!
파이썬 logging 모듈 소개 🛠️
파이썬은 이러한 로깅 기능을 표준 라이브러리인 logging 모듈을 통해 강력하게 지원합니다. 별도의 설치 없이 import logging 한 줄이면 바로 사용할 수 있죠! 이 모듈은 매우 유연해서 간단한 스크립트부터 복잡한 대규모 애플리케이션까지 다양한 로깅 요구사항을 만족시킬 수 있습니다.
기본 로깅 설정과 출력 레벨 ⚙️

logging 모듈은 다양한 심각도(Severity)에 따라 로그 메시지를 구분하며, 이를 **로그 레벨(Log Level)**이라고 합니다. 기본적으로 5가지 표준 레벨이 있습니다 (심각도가 낮은 순서대로):
- DEBUG: 상세한 진단 정보. 개발 단계에서 문제 해결에 유용.
- INFO: 일반적인 정보. 프로그램의 주요 진행 상황을 알림.
- WARNING: 예상치 못한 일이나 잠재적인 문제 발생 가능성. 프로그램은 계속 동작. (기본 레벨)
- ERROR: 심각한 문제로 인해 프로그램의 일부 기능이 제대로 동작하지 않음.
- CRITICAL: 매우 심각한 오류로 인해 프로그램 자체가 계속 실행될 수 없을 정도.
기본적으로 logging 모듈은 WARNING 레벨 이상의 로그만 출력합니다.
import logging
# 기본 설정에서는 WARNING 레벨 이상의 로그만 출력됩니다.
logging.debug("이 메시지는 보이지 않아요. (DEBUG)")
logging.info("이 메시지도 보이지 않아요. (INFO)")
logging.warning("이 메시지는 보입니다! (WARNING)")
logging.error("오류 발생! 이 메시지도 보입니다. (ERROR)")
logging.critical("심각한 오류! 당연히 보입니다. (CRITICAL)")
모든 레벨의 로그를 보고 싶거나, 특정 레벨 이상의 로그만 보고 싶다면 logging.basicConfig() 함수를 사용해 설정할 수 있습니다. 이 함수는 로깅 시스템이 처음 사용될 때 단 한 번만 호출해야 효과가 있습니다.
import logging
# 로깅 기본 설정 (레벨, 출력 포맷 등)
# 프로그램 시작 부분에서 한 번만 설정하는 것이 일반적입니다.
logging.basicConfig(level=logging.DEBUG) # DEBUG 레벨 이상의 모든 로그를 출력
logging.debug("이제 DEBUG 메시지도 잘 보입니다!")
logging.info("INFO 메시지도 물론 잘 보이고요!")
로그 메시지, 보기 좋게 꾸미기: 출력 결과 Formatting 🎨
단순히 메시지만 출력하는 것보다 언제, 어떤 모듈에서, 어떤 레벨로 발생한 로그인지 함께 보여주면 훨씬 유용하겠죠? basicConfig() 함수의 format 인자를 사용하면 로그 출력 형식을 지정할 수 있습니다.
자주 사용되는 포맷팅 옵션은 다음과 같습니다:
- %(asctime)s: 로그가 기록된 시간 (예: 2023-10-27 14:30:00,123)
- %(levelname)s: 로그 레벨 (예: DEBUG, INFO, WARNING)
- %(name)s: 로거의 이름 (기본값은 root)
- %(module)s: 로그를 발생시킨 모듈 이름
- %(funcName)s: 로그를 발생시킨 함수 이름
- %(lineno)d: 로그를 발생시킨 코드 라인 번호
- %(message)s: 실제 로그 메시지
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - [%(module)s:%(lineno)d] - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S' # 시간 형식 지정
)
logging.info("포맷이 적용된 정보성 로그입니다.")
logging.warning("포맷이 적용된 경고 로그입니다.")
def my_function():
logging.debug("이건 my_function 안의 디버그 로그예요.") # 레벨이 DEBUG라 출력 안됨
logging.error("my_function에서 오류가 발생했어요!")
my_function()
위 코드를 실행하면, INFO와 ERROR 레벨의 로그가 지정된 포맷으로 출력되는 것을 볼 수 있습니다. 시간, 레벨, 모듈명, 라인 번호까지 함께 나오니 훨씬 유용하죠?
오류의 순간 포착! Stack Traces 캡처하기 🔍
프로그램에서 예외(Exception)가 발생했을 때, 단순히 오류 메시지만 기록하는 것보다 어떤 호출 과정을 거쳐 오류가 발생했는지 알려주는 스택 트레이스(Stack Trace) 정보가 있다면 디버깅에 매우 큰 도움이 됩니다. logging 모듈은 이를 쉽게 기록할 수 있도록 지원합니다.
- logging.error(), logging.critical() 등의 메소드에 exc_info=True 인자를 전달하는 방법.
- try...except 블록 내에서 logging.exception() 메소드를 사용하는 방법 (자동으로 ERROR 레벨로 로깅하며 스택 트레이스 포함).
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
try:
result = 10 / 0
except ZeroDivisionError:
logging.error("0으로 나누려고 시도했습니다!", exc_info=True)
print("-" * 30)
try:
my_list = []
item = my_list[0]
except IndexError as e:
# logging.exception()은 ERROR 레벨로 로그를 남기며, 예외 정보를 자동으로 포함합니다.
logging.exception(f"리스트 인덱스 오류 발생! (예외: {e})")
이렇게 하면 예외 발생 시 스택 트레이스 정보가 로그에 함께 기록되어, 문제의 원인을 훨씬 빠르게 파악할 수 있습니다.
logging 모듈의 핵심 구성 요소: 클래스와 함수들 🧩
logging 모듈은 사실 여러 객체들이 협력하여 동작하는 구조입니다. 주요 구성 요소는 다음과 같습니다:
- Loggers (로거): 로그 메시지를 애플리케이션 코드에서 직접 받아 처리하는 객체입니다. logging.getLogger(name) 함수로 특정 이름의 로거 인스턴스를 얻을 수 있습니다. 이름을 지정하지 않거나 root로 지정하면 루트 로거를 사용합니다. 로거는 계층 구조를 가질 수 있습니다.
- Handlers (핸들러): 로거로부터 받은 로그 메시지(정확히는 LogRecord 객체)를 적절한 목적지(콘솔, 파일, 네트워크 등)로 보내는 역할을 합니다. 다양한 종류의 핸들러가 미리 정의되어 있습니다 (예: StreamHandler, FileHandler, SocketHandler, SMTPHandler 등).
- Formatters (포맷터): 로그 메시지의 최종 출력 형식을 지정합니다. 앞에서 basicConfig의 format 인자로 본 것이 바로 이것입니다. logging.Formatter 클래스를 사용해 생성합니다.
- Filters (필터): 어떤 로그 메시지를 핸들러로 전달할지 또는 로거에서 처리할지 세밀하게 제어할 수 있는 기능을 제공합니다 (이번 강의에서는 깊게 다루지 않습니다).
- LogRecords: 로그 이벤트에 대한 모든 정보를 담고 있는 객체입니다. 로거가 로그 메시지를 받으면 이 객체를 생성하여 핸들러와 필터로 전달합니다. (직접 다룰 일은 적습니다.)
이러한 구성 요소들을 조합하여 매우 유연하고 강력한 로깅 시스템을 구축할 수 있습니다.
로그를 어디에나! 다양한 Handler 사용하기 📬
basicConfig()는 편리하지만, 로그를 콘솔과 파일에 동시에 남기거나, 특정 로거에만 다른 설정을 적용하는 등 복잡한 요구사항에는 한계가 있습니다. 이럴 때 핸들러를 직접 설정합니다.
가장 많이 사용되는 핸들러는 다음과 같습니다:
- StreamHandler: 로그를 스트림(예: sys.stdout, sys.stderr - 즉, 콘솔)으로 보냅니다.
- FileHandler: 로그를 파일로 보냅니다.
import logging
# 1. 로거 생성 (이름으로 구분, __name__은 현재 모듈의 이름을 사용)
# 이렇게 하면 다른 모듈에서 로깅 설정을 분리하기 용이합니다.
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) # 로거 자체의 최소 처리 레벨 설정
# 2. 핸들러 생성
# 콘솔 핸들러
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) # 콘솔에는 INFO 레벨 이상만 출력
# 파일 핸들러
# 'app.log' 파일에 기록, 파일이 이미 있으면 덮어쓰기('w'), 한글 처리를 위해 encoding='utf-8'
file_handler = logging.FileHandler('app.log', mode='w', encoding='utf-8')
file_handler.setLevel(logging.DEBUG) # 파일에는 DEBUG 레벨 이상 모두 기록
# 3. 포맷터 생성
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 4. 각 핸들러에 포맷터 설정
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 5. 로거에 핸들러 추가
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 이제 logger를 사용하여 로그를 남깁니다.
logger.debug("이 메시지는 파일에만 기록됩니다 (콘솔 핸들러 레벨이 INFO).")
logger.info("이 메시지는 콘솔과 파일 모두에 기록됩니다.")
logger.warning("이 메시지도 콘솔과 파일 모두에 기록됩니다.")
logger.error("오류 발생! 콘솔과 파일 모두에 기록됩니다.")
# 만약 다른 모듈에서 이 로거 설정을 사용하고 싶다면,
# import logging
# logger = logging.getLogger(__name__) # 와 같이 동일한 이름으로 로거를 가져와 사용하면 됩니다.
위 예제에서는 __name__ (현재 모듈의 이름, 예를 들어 __main__)으로 로거를 생성하고, 콘솔과 파일에 각각 다른 레벨로 로그를 출력하도록 설정했습니다. 이렇게 하면 개발 중에는 DEBUG 레벨까지 파일에 자세히 기록하고, 콘솔에는 INFO 레벨 이상의 주요 정보만 간략히 볼 수 있어 편리합니다.
더욱 정교한 제어: Other configurations (fileConfig, dictConfig) 🗂️
애플리케이션의 규모가 커지고 로깅 설정이 복잡해지면, 코드 내에서 basicConfig나 핸들러를 직접 설정하는 것보다 설정 파일이나 딕셔너리를 사용하는 것이 더 효율적일 수 있습니다. logging 모듈은 이를 위한 두 가지 방법을 제공합니다:
- logging.config.fileConfig(fname): INI 스타일의 설정 파일을 읽어 로깅 시스템을 구성합니다.
- logging.config.dictConfig(config): 파이썬 딕셔너리 형태로 정의된 설정을 읽어 로깅 시스템을 구성합니다. 이 방법이 더 유연하고 현대적인 방식으로 권장됩니다.
이 방법들은 로거, 핸들러, 포맷터를 각각 정의하고 연결하는 복잡한 설정을 코드와 분리하여 관리할 수 있게 해줍니다. 이번 입문 과정에서는 자세히 다루지 않지만, 이런 기능이 있다는 것을 알아두시면 나중에 큰 프로젝트를 진행할 때 유용하게 활용하실 수 있을 거예요!
정리하며 📝
오늘은 프로그램의 보이지 않는 곳에서 묵묵히 제 역할을 수행하는 '로깅'에 대해 배웠습니다!
- Logging의 중요성 📢: 디버깅, 모니터링, 감사 추적 등 프로그램의 안정성과 신뢰성을 높이는 핵심 요소입니다.
- logging 모듈 🛠️: 파이썬의 강력한 표준 로깅 라이브러리입니다.
- 로그 레벨 (DEBUG, INFO, WARNING, ERROR, CRITICAL) ⚙️: 메시지의 심각도를 구분합니다.
- basicConfig(): 기본적인 로깅 설정을 쉽게 할 수 있습니다. (레벨, 포맷 등)
- 출력 포맷팅 🎨: %(asctime)s, %(levelname)s 등으로 로그 메시지를 풍부하게 만듭니다.
- Stack Traces 캡처 🔍: exc_info=True 또는 logging.exception()으로 오류의 근원을 추적합니다.
- 핵심 구성 요소 (Logger, Handler, Formatter) 🧩: 유연한 로깅 시스템의 기반입니다.
- 다양한 Handlers (특히 FileHandler, StreamHandler) 📬: 로그를 다양한 목적지로 보낼 수 있습니다.
- 고급 설정 (fileConfig, dictConfig) 🗂️: 복잡한 로깅 설정을 코드와 분리하여 관리합니다.
지난 시간까지 프로그램에 기억과 소통 능력을 부여했다면, 오늘 배운 로깅은 프로그램에 '자기 성찰' 능력과 '의사소통(개발자에게)' 능력을 더해주는 것과 같습니다. 🕵️♂️🗣️ 이제 여러분의 프로그램은 문제가 발생했을 때 스스로 단서를 남기고, 개발자는 그 단서를 통해 문제를 해결하며 더욱 견고한 소프트웨어를 만들 수 있게 되었습니다.
오늘로서 [패스트캠퍼스] 파이썬 기본 과정의 여정이 마무리되었습니다. 변수와 자료형부터 시작해서 제어문, 함수, 객체 지향 프로그래밍, 모듈, 예외 처리, 그리고 오늘 배운 로깅까지! 정말 긴 여정을 함께 달려오셨네요. 파이썬이라는 강력하고 매력적인 언어의 기본기를 탄탄히 다지셨으니, 이제 여러분의 상상력과 아이디어를 프로그램으로 멋지게 구현해 나갈 준비가 되었습니다! 🚀✨

오늘도 정말 수고 많으셨습니다! 💪 파이썬과 함께하는 여러분의 빛나는 앞날을 응원합니다! 즐거운 코딩 여정 되시길 바랍니다! 👋
'데이터 엔지니어링' 카테고리의 다른 글
| 패스트캠퍼스 환급챌린지 47일차: 데이터엔지니어링 초격차 강의 후기 (0) | 2025.05.18 |
|---|---|
| 패스트캠퍼스 환급챌린지 46일차: 데이터엔지니어링 초격차 강의 후기 (1) | 2025.05.16 |
| 패스트캠퍼스 환급챌린지 44일차: 데이터엔지니어링 초격차 강의 후기 (0) | 2025.05.14 |
| 패스트캠퍼스 환급챌린지 43일차: 데이터엔지니어링 초격차 강의 후기 (0) | 2025.05.13 |
| 패스트캠퍼스 환급챌린지 42일차: 데이터엔지니어링 초격차 강의 후기 (0) | 2025.05.12 |