logging을 통해 프로그램 동작 상태 등을 로그로 남길 경우,
프로그램의 사후 진단을 보다 효과적으로 할 수 있기 때문에,
문제 분석이나 디버깅 등에 유용하게 사용할 수 있음.
logging을 사용할 경우,
- 소스 코드의 수정 없이
- 모든 정보를 한꺼번에 출력하는 것이 아닌,
- 원하는 정보 레벨(log level로 지정) 이상의 로그 메시지를
- 관련 이벤트 발생 시점과 함께 기록하는 것이 가능함.
장점
print문 을 사용하는 경우보다 다음의 장점을 가짐
- log 기록을 여러 곳에 동시에 전송가능.
- 출력 위치별로 다른 포맷을 지정할 수 있음.
- 프로그램 실행 중에도 설정 변경을 통한 로그 출력방식 제어 가능 (동적 변경).
- 계층적 logger를 생성하여 로그 발생위치를 명확하게 파악 가능.
- Lazy Formatting을 통해 실제 log가 기록되는 경우에만 연산이 수행됨.
log levels
Python의 logging은 다음의 5단계의 levels를 지원함.
낮은 단계의 log level이 지정된 경우 보다 높은(=심각한) log levels가 같이 기록됨.
| Level | 언제 사용되는가? |
| DEBUG | 가장 낮은 심각도와 가장 상세한 수준의 정보를 가지고 있음. 주로 디버깅 등에서 문제의 원인을 파악하는데 사용됨. |
| INFO | 프로그램이 정상적으로 작동하는지를 tracking하기 위한 메시지들. |
| WARNING | 예상치 못한 event 또는 추후 문제가 될 수 있는 경우에 사용됨. |
| ERROR | 프로그램이 의도한 기능을 수행하지 못하는 수준의 심각한 에러 발생시 사용. |
| CRITICAL | 가장 심각한 수준의 에러가 발생한 경우로 프로그램의 실행이 중단될 수 있는 수준을 의미. |

간단한 사용법
logging.basicConfig 로 최초로 한번만 설정해주고 난 이후에,
logging.debug, logging.info, logging.warning, logging.error, logging.critical 등을 통해 로깅 메시지를 남김.
import logging
logging.basicConfig(
format='%(asctime)s %(levename)s:%(message)s', # Formatter생성
level=logging.INFO,
datefmt='%m-%d-%Y %I:%M:%S %p',
# 기본 Handler를 이용(콘솔에 출력하는 Stream Handler)
) # 가장 기본적인 로깅환경을 이용- root logger를 사용함.
logging.debug('debug message example!')
- root logger를 이용하는 가장 간단한 형태.
- 내부적으로는 아래 설명하는 핵심 클래스 객체들이 만들어지고 연결되어 동작.
- logger = logging.getLogger(__name__) 과 같이 모듈(각 py 파일)별로 logger를 생성하기도 함.
- 이는 root logger의 자식 로거임.
핵심 클래스들

- Logger: 로깅 모듈의 중심. 이 클래스의 인스턴스를 통해 로깅 시스템을 제어함. 무엇을 기록할지를 결정함.
- Handler: 어디에 기록할지를 결정하는 역할. 파일에 기록할지 콘솔에 출력할지 네트워크로 전송할 지 등등
- Formatter: 어떻게 기록할지를 결정함. what, where, when 등의 내용을 어떤 형식으로 기록할지를 결정.
앞서의 예제 코드는 다음과 같음:
import logging
# ----------------- [설정 단계] -----------------
# 1. 로거 객체 가져오기
# 이름을 비워두면 'root logger'를 반환.
# Application의 main logger를 설정하기 위해 사용하는 방식.
logger = logging.getLogger()
logger.setLevel(logging.INFO) # 로거의 레벨 설정
# 2. Handler 생성하기 (로그를 어디로 보낼지)
stream_handler = logging.StreamHandler() # 콘솔(화면)으로 보낼 핸들러
# 3. Formatter 생성하기 (로그를 어떤 형식으로 남길지)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 4. Handler와 Formatter 연결하기
stream_handler.setFormatter(formatter)
# 5. Logger와 Handler 연결하기
logger.addHandler(stream_handler)
# -----------------------------------------------
logging.debug('debug message example!')
2개의 handler와 다른 레벨를 이용한 경우는 다음과 같음:
import logging
# log msg를 어떻게 출력할지를 Formatter객체로 설정 (다음 절 참고).
formatter = logging.Formatter('%(asctime)s~%(levelname)s~%(message)s~module:%(module)s')
# File Handler 객체 생성.
file_handler = logging.FileHandler("logfile.log")
file_handler.setLevel(logging.WARN) # WARN 이상의 log를 파일로 저장.
file_handler.setFormatter(formatter)
# Console의 출력위한 StreamHandler 객체 생성.
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG) # DEBUG이상의 log를 콘솔로 출력
console_handler.setFormatter(formatter)
# Root Logger생성하고 서로 연결.
logger = logging.getLogger()
logger.addHandler(file_handler)
logger.addHandler(console_handler)
logger.setLevel(logging.DEBUG) # Logger가 우선 DEBUG이상의 log를 handler에 전달.
# 다양한 레벨의 로그 남기기.
logger.critical("Something critical")
logger.error("An error")
logger.warning("A warning")
logger.info("My info is that you are here")
logger.debug("I'm debugging")
logging message format attributes
로그 레코드의 format에 사용되는 attributes 중 많이 사용되는 것을 간략히 정리함.
| Attributer Name | Format | Description |
| asctime | %(asctime)s | 사람이 읽을 수 있는 로그 레코드가 생성된 시간 2023-12-18 18:20:55, 810 형태임 (쉼표 뒤의 숫자는 밀리세컨드) |
| created | %(created)f | time.time()이 반환하는 시간으로 로그 레코드가 생성된 시간 |
| filename | %(filename)s | pathname 파일명 부분 |
| funcName | %(funcName)s | 로깅 호출을 포함하는 function의 이름 |
| levelname | %(levelname)s | Logging Level |
| lineno | %(lineno)d | 로깅 호출이 일어난 소스의 line number |
| message | %(message)s | 로깅 메시지. msg % args 의 형태임. |
| module | %(module)s | 모듈 (filename 에서 확장자 뺀 이름) |
| name | %(name)s | 사용된 logger의 이름. |
| pathname | %(pathname)s | 로깅호출이 일어난 소스 파일의 전체 경로명 |
| process | %(process)d | process ID |
| processName | %(processName)s | process name |
| thread | %(thread)d | thread ID |
| threadName | %(threadName)s | thread name |
예제: 동적인 로깅 레벨 변경
import logging
import time
def setup_logger():
"""초기 로거 설정을 담당하는 함수"""
# 1. 로거 생성 (이름을 지정하여 루트 로거와 분리)
logger = logging.getLogger("my_app")
# 중복 추가를 방지하기 위해, 핸들러가 이미 있는지 확인
if logger.hasHandlers():
return logger
# 2. 초기 레벨 설정 (INFO로 시작)
logger.setLevel(logging.INFO)
print("--- 초기 로거 레벨을 INFO로 설정! ---")
# 3. 핸들러 및 포매터 설정
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# 4. 로거에 핸들러 추가
logger.addHandler(handler)
return logger
def some_business_logic(logger):
"""애플리케이션의 핵심 로직을 흉내 내는 함수"""
logger.info("핵심 로직을 시작!.")
# 이 부분은 현재 로거 레벨에서는 출력되지 않을 것입니다.
logger.debug("상세 디버그 정보: 데이터 처리 준비 완료.")
time.sleep(1) # 무언가 작업하는 것처럼 잠시 대기
logger.info("핵심 로직을 성공적으로 수행함.")
# --- 메인 실행 부분 ---
if __name__ == "__main__":
# 1. 초기 로거 설정
my_logger = setup_logger()
# 2. 초기 상태에서 로직 실행
print("\n[1. 초기 상태에서 로직 실행]")
some_business_logic(my_logger)
# 3. 사용자 입력을 받아 동적으로 레벨 변경
print("\n" + "="*40)
print("이제 로거 레벨을 DEBUG로 변경하여 더 상세한 로그를 확인함!!!.")
user_input = input("디버그 모드를 활성화하려면 'd'를 누르고 Enter 키를 입력할 것: ")
if user_input.lower() == 'd':
# --- 여기가 동적 설정 변경의 핵심 ---
print("\n--- 로거 레벨을 DEBUG로 동적으로 변경되었음! ---")
my_logger.setLevel(logging.DEBUG)
# 4. 변경된 상태에서 로직 다시 실행
print("\n[2. DEBUG 모드로 변경 후 로직 재실행]")
some_business_logic(my_logger)
# 레벨을 다시 원래대로 돌릴 수도 있습니다.
print("\n--- 로거 레벨을 다시 INFO로 복구함. ---")
my_logger.setLevel(logging.INFO)
else:
print("\n디버그 모드를 활성화하지 않고 종료.")
보다 자세한 건 다음 URL 참고
https://docs.python.org/3/library/logging.html#logrecord-attributes
logging — Logging facility for Python
Source code: Lib/logging/__init__.py Important: This page contains the API reference information. For tutorial information and discussion of more advanced topics, see Basic Tutorial, Advanced Tutor...
docs.python.org
https://www.codemotion.com/magazine/ai-ml/big-data/logging-in-python-a-broad-gentle-introduction/
Logging in Python: a broad, gentle introduction
A fundamental step in writing production-quality code is the ability to log properly. In this article, I will explain how to log in Python.
www.codemotion.com
References
https://docs.python.org/3/library/logging.html#module-logging
logging — Logging facility for Python
Source code: Lib/logging/__init__.py Important: This page contains the API reference information. For tutorial information and discussion of more advanced topics, see Basic Tutorial, Advanced Tutor...
docs.python.org
https://hwangheek.github.io/2019/python-logging/
조금 더 체계적인 Python Logging
IntroductionPython으로 코드를 짤 때, 로그를 띄우는 방법으로 print('[*] Message')를 정말 많이 써 왔습니다. 군더더기 없고, 유연하고, dependency 없이 아무 위치에나 넣을 수 있다는 점이 좋았습니다. ‘
hwangheek.github.io
https://www.codemotion.com/magazine/ai-ml/big-data/logging-in-python-a-broad-gentle-introduction/
Logging in Python: a broad, gentle introduction
A fundamental step in writing production-quality code is the ability to log properly. In this article, I will explain how to log in Python.
www.codemotion.com
'Python' 카테고리의 다른 글
| [Term] Agile Programming Language : Agile Development (=Programming) (0) | 2024.01.06 |
|---|---|
| [Python] Debugging : Traceback, Stacktrace, Backtrace ... (0) | 2023.12.25 |
| [Python] Terminal, WSL, Conda, and VSCode (1) | 2023.12.15 |
| [Etc] Token and Tokenizer (2) | 2023.12.06 |
| [Python] File Handling (2) | 2023.12.05 |