Python Logging 사용 방법 정리
1. logging의 기본 개념
1) 개요
- 목적: 디버깅, 모니터링, 문제 해결을 위한 로그 기록
- 특징: 다중 로거, 핸들러 및 포맷터 지원, 로그 레벨 설정, 필터링 등
- 기본 구성 요소:
- Logger: 로그 생성 및 관리, 핸들러 등록/제거, 레벨 설정 등
- Handler: 로그 출력 대상 설정, 각종 핸들러(콘솔, 파일, 순환 파일 등) 제공
- Formatter: 로그 메시지 출력 형식 지정 (날짜, 로거 이름, 레벨, 메시지 등)
- Filter: 로그 레코드에 대해 추가 조건을 적용하여 출력 여부 결정
2. 로깅 시스템 구성 요소
1) Logger
- 정의: 로깅 시스템의 중심 객체로, 애플리케이션 코드에서 로그 메시지를 남길 때 사용한다.
- 생성 방법: logging.getLogger(name)
- name: 문자열로 로거의 이름을 지정한다. (빈 문자열은 root logger를 의미)
- 로그 레벨
-DEBUG (10): 상세한 정보, 문제 진단 시 사용
-INFO (20): 정상 동작 확인을 위한 정보
-WARNING (30): 예상치 못한 일이 발생했거나 가까운 미래에 문제가 발생할 수 있는 경우
-ERROR (40): 심각한 문제로 인해 프로그램의 일부 기능이 동작하지 않는 경우
-CRITICAL (50): 프로그램이 실행을 계속할 수 없는 심각한 에러
- 로거 계층 구조
- 로거는 계층적 구조를 가지며, 점(.)으로 구분된 이름을 사용한다.
logger = logging.getLogger('main.sub.module')
- 로거 생성 및 설정
import logging
# 기본 로거 설정
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='app.log'
)
# 로거 생성
logger = logging.getLogger(__name__)
2) Handler
- 정의: Logger에서 생성된 로그 메시지를 최종 목적지(콘솔, 파일, 네트워크 등)로 전송하는 객체이다.
- 종류:
- StreamHandler: 콘솔(표준 출력 등)에 로그 출력
- FileHandler: 파일에 로그 기록
- RotatingFileHandler, TimedRotatingFileHandler: 파일 크기나 시간에 따라 로그 파일을 순환하며 관리
- 기타: SMTPHandler, SysLogHandler, HTTPHandler 등
- 핸들러 설정 예시
import logging
from logging.handlers import RotatingFileHandler
# 파일 핸들러 생성
file_handler = RotatingFileHandler(
'app.log',
maxBytes=1024*1024, # 1MB
backupCount=5
)
file_handler.setLevel(logging.INFO)
# 콘솔 핸들러 생성
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
# 포맷터 설정
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 로거에 핸들러 추가
logger = logging.getLogger(__name__)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
3) Formatter
- 정의: 로그 메시지의 출력 형식을 지정한다.
- 역할: 날짜/시간, 로그 레벨, 메시지, 로거 이름 등 다양한 정보를 포함하도록 포맷팅 한다.
- 포멧터 설정
# 포맷터 설정
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
4) Filter
- 정의: 로깅 레코드가 최종 출력되기 전에 추가적인 조건으로 걸러낼 수 있는 객체이다.
- 용도: 특정 모듈이나 로그 레벨, 또는 사용자 정의 조건에 따라 로그 메시지를 선택적으로 출력한다.
- 기본 필터
class InfoFilter(logging.Filter):
def filter(self, record):
return record.levelno == logging.INFO
logger.addFilter(InfoFilter())
- 필터 적용 예시
def email_filter(record):
return record.levelno >= logging.ERROR
handler = logging.StreamHandler()
handler.addFilter(email_filter)
3. Logger 클래스
1) 주요 메소드 및 파라미터
1. logging.getLogger(name)
- 설명: 지정된 이름을 가진 Logger 객체를 반환합니다. 동일한 이름을 사용하면 동일한 인스턴스가 반환된다.
- 파라미터: name (문자열)
2. Logger.setLevel(level)
- 설명: Logger의 최소 로그 레벨을 설정한다. 이 레벨보다 낮은 로그는 무시된다.
- 파라미터: level (예: logging.DEBUG, logging.INFO 등)
3. 로그 메시지 기록 메소드:
- Logger.debug(msg, *args, **kwargs)
- Logger.info(msg, *args, **kwargs)
- Logger.warning(msg, *args, **kwargs)
- Logger.error(msg, *args, **kwargs)
- Logger.critical(msg, *args, **kwargs)
- Logger.exception(msg, *args, exc_info=True, **kwargs)
- exception 메소드는 예외 정보를 자동으로 포함하여 ERROR 레벨 로그를 남긴다.
try:
# 위험한 작업
raise ValueError("잘못된 값")
except Exception as e:
logger.exception("에러 발생") # 자동으로 스택 트레이스 포함
# 또는
logger.error("에러 발생: %s", str(e), exc_info=True)
4. Logger.log(level, msg, *args, **kwargs)
- 설명: 지정한 레벨로 로그 메시지를 기록한다.
- 파라미터:
- level: 로그 레벨 (정수 값 또는 상수)
- msg: 로그 메시지
- *args, **kwargs: 메시지 포맷에 사용될 추가 인자
5. Handler 관리 메소드:
- Logger.addHandler(handler)
- Logger에 핸들러를 추가한다.
- Logger.removeHandler(handler)
- Logger에서 핸들러를 제거한다.
- Logger.hasHandlers()
- Logger에 등록된 핸들러가 있는지 여부를 반환한다.
6. 기타 메소드:
- 내부적으로 사용되는 makeRecord(), findCaller() 등은 로그 레코드 생성에 관여하지만 일반 사용자 수준에서는 주로 위의 메소드들을 사용한다.
7. extra 파라미터:
logger.info('메시지', extra={'ip': '123.45.67.89'})
4. Handler 클래스
- Handler는 로그 메시지를 특정 출력 대상으로 전달하는 역할을 한다.
1) 공통 메소드 및 파라미터
- Handler.setLevel(level)
- 설명: 해당 핸들러의 로그 레벨을 설정한다.
- 파라미터: level (예: logging.DEBUG, logging.INFO 등)
- Handler.setFormatter(fmt)
- 설명: 로그 메시지의 출력 형식을 지정하는 Formatter 객체를 설정한다.
- 파라미터: fmt (Formatter 인스턴스)
- Handler.addFilter(filter)
- 설명: 특정 조건에 따라 로그 메시지의 출력 여부를 결정할 수 있는 Filter 객체를 추가한다.
- 파라미터: filter (Filter 인스턴스)
- Handler.removeFilter(filter)
- 설명: 추가된 Filter 객체를 제거한다.
- 핸들러 처리 메소드:
- Handler.handle(record)
- 로그 레코드를 처리하며, 레벨 및 필터 조건 등을 확인한다.
- Handler.emit(record)
- 실제로 로그 메시지를 출력하는 메소드로, 각 Handler마다 구현이 다르다.
- Handler.handleError(record)
- 로그 출력 중 발생한 예외를 처리한다.
- Handler.close()
- 핸들러를 종료하고 자원을 정리한다.
2) 주요 핸들러 종류와 생성자 파라미터
1. StreamHandler
- 설명: 콘솔이나 파일 객체와 같은 스트림으로 로그 출력
- 생성자: logging.StreamHandler(stream=None)
- stream: 출력 대상 스트림 (기본값은 sys.stderr)
2. FileHandler
- 설명: 로그 메시지를 파일에 기록
- 생성자: logging.FileHandler(filename, mode='a', encoding=None, delay=False)
- filename: 로그를 저장할 파일 이름
- mode: 파일 열기 모드 (기본 'a' – append)
- encoding: 파일 인코딩
- delay: True일 경우 실제 파일 열기를 지연
3. RotatingFileHandler
- 설명: 파일 크기가 일정 수준에 도달하면 새 파일로 순환하며 로그 기록
- 생성자:
- maxBytes: 파일 최대 크기 (바이트 단위)
- backupCount: 보관할 백업 파일 수
logging.handlers.RotatingFileHandler(
filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False
)
4. TimedRotatingFileHandler
- 설명: 시간 간격에 따라 로그 파일을 순환하며 관리
- 생성자:
- when: 순환 기준 시간 단위 (예: 's', 'm', 'h', 'd', 'midnight', 'W0'-'W6')
- interval: 시간 간격
- backupCount: 보관할 백업 파일 수
- utc: UTC 시간을 사용할지 여부
logging.handlers.TimedRotatingFileHandler(
filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False
)
5. Formatter 클래스
- Formatter는 로그 메시지의 최종 출력 형식을 지정한다.
1) 생성자 및 파라미터
- Formatter(fmt=None, datefmt=None, style='%')
- fmt: 로그 메시지 형식 문자열
- 예: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
- datefmt: 날짜/시간 형식 문자열
- 예: '%Y-%m-%d %H:%M:%S'
- style: 포맷 스타일 지정 ('%', '{', 또는 '$')
2) 주요 메소드
- format(record)
- 설명: 전달된 로그 레코드(LogRecord)를 지정된 형식 문자열에 따라 포맷하여 문자열로 반환
- formatTime(record, datefmt=None)
- 설명: 로그 레코드의 생성 시간을 지정한 날짜 형식으로 포맷
- formatException(exc_info)
- 설명: 예외 정보(exc_info)를 포맷하여 문자열로 반환
6. Filter 클래스
- Filter는 Logger 또는 Handler에 추가되어 특정 조건에 맞는 로그 레코드만 통과시키는 역할을 한다.
1) 생성자 및 기본 사용법
- Filter(name='')
- 설명: name 매개변수를 지정하면 해당 이름(또는 이름 접두사)을 가진 로거에 대해서만 필터링이 적용된다.
- 예: Filter('my_module')는 로거 이름이 'my_module' 또는 그 하위인 경우에 적용된다.
2) 주요 메소드
- filter(record)
- 설명: 로그 레코드를 인자로 받아, 해당 레코드를 출력할지 여부를 결정하는 불리언 값을 반환한다.
- 반환 값: True이면 로그 메시지가 출력되고, False이면 무시된다.
7. 고급 설정
1) 로깅 설정 파일 사용
- fileConfig(): INI 형식의 구성 파일을 사용해 로깅 설정을 불러온다.
- dictConfig(): 파이썬 딕셔너리 형태로 로깅 설정을 구성할 수 있다.
- 두 방법 모두 여러 로거, 핸들러, 포맷터, 필터를 한 번에 설정할 수 있어 복잡한 로깅 환경에 유용하다.
2) 커스텀 로깅
- 사용자 정의 Logger/Handler/Formatter/Filter: 필요에 따라 기본 클래스를 상속하여 기능을 확장할 수 있다.
- 로깅 레벨 추가: logging.addLevelName(level, levelName)를 통해 사용자 정의 로그 레벨을 추가할 수 있다.
8. 예제 코드
- 다음은 Logger, Handler, Formatter, Filter를 활용한 예제 코드이다.
- 주요 설명:
- Logger 설정: myLogger 이름의 Logger를 생성하고 레벨을 DEBUG로 설정
- Handler 구성: 콘솔 출력을 위한 StreamHandler와 파일 출력을 위한 FileHandler를 각각 생성 후 레벨과 Formatter를 설정
- Filter 적용: 사용자 정의 Filter를 통해 메시지 내에 특정 단어("SECRET")가 포함된 경우 로그를 걸러냄
- 로그 메시지 기록: 각 레벨별로 메시지를 기록, 예외 발생 시 logger.exception()을 활용하여 스택 트레이스까지 출력
import logging
import logging.handlers
# 사용자 정의 Filter: 메시지에 특정 단어가 포함되면 로그 출력하지 않음
class NoKeywordFilter(logging.Filter):
def __init__(self, keyword, name=""):
super().__init__(name)
self.keyword = keyword
def filter(self, record):
# 로그 메시지에 지정된 keyword가 없으면 True 반환
return self.keyword not in record.getMessage()
# 1. Logger 객체 생성 (이름이 'myLogger'인 로거)
logger = logging.getLogger('myLogger')
logger.setLevel(logging.DEBUG) # DEBUG 레벨 이상 모든 메시지 기록
# 2. StreamHandler 생성 (콘솔 출력)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.DEBUG)
# 3. FileHandler 생성 (파일에 로그 기록)
file_handler = logging.FileHandler('app.log', mode='a', encoding='utf-8')
file_handler.setLevel(logging.INFO) # INFO 이상 메시지만 기록
# 4. Formatter 생성 (출력 형식 지정)
formatter = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 각 핸들러에 Formatter 설정
stream_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 5. Filter 생성 및 핸들러에 추가 (예: "SECRET" 단어가 포함된 메시지 필터링)
keyword_filter = NoKeywordFilter(keyword='SECRET')
stream_handler.addFilter(keyword_filter)
file_handler.addFilter(keyword_filter)
# 6. Logger에 핸들러 추가
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
# 7. 다양한 로그 메시지 기록
logger.debug("이 메시지는 디버그 정보입니다.")
logger.info("정보 메시지입니다.")
logger.warning("경고 메시지입니다.")
logger.error("오류 메시지입니다.")
logger.critical("치명적인 메시지입니다.")
# 예외 발생 시 exception 메소드 사용
try:
1 / 0
except Exception as e:
logger.exception("예외가 발생했습니다: %s", e)
# 필터 테스트: "SECRET" 단어가 포함된 메시지는 출력되지 않음
logger.info("이 메시지에는 SECRET 단어가 포함되어 있습니다. (출력되지 않음)")
9. 실무 예제 코드
- 아래는 FastAPI 애플리케이션에서 파이썬 표준 logging 모듈을 활용하여 세 가지 “로그 뷰”를 구성하는 샘플 코드이다.
1) 각 로그 뷰는 아래와 같이 동작한다.
- console_view: 모든 레벨의 로그를 콘솔에 출력한다.
- logfile_view: 오직 ERROR 레벨의 로그만 기록하며, 로그 파일명은 _logfile.txt_이다. (매일 자정마다 새 파일로 교체)
- logfile_with_console: 오직 INFO 레벨의 로그만 기록하는데, 파일(log_with_console.txt)과 콘솔 모두에 출력된다. (파일은 매일 자정마다 새로 생성)
- 또한, 실무에서 자주 사용하는 설정(로그 포맷, 레벨 설정, 파일 핸들러의 일별 회전 등)과 로그 필터를 직접 구현하여 정확한 레벨의 로그만 남길 수 있도록 한다.
2) 주요 설정 및 메소드 설명
1. Logger 생성 및 레벨 설정
- logging.getLogger(name)을 통해 각 로그 뷰별로 별도의 로거를 생성한다.
- setLevel() 메소드를 사용하여 해당 로거가 처리할 최소 로그 레벨을 지정한다.
2. Handler 생성
- StreamHandler: 콘솔(표준 출력)로 로그를 출력한다.
- TimedRotatingFileHandler: 지정한 시간(여기서는 매일 자정)을 기준으로 파일을 교체하며 로그를 기록한다.
3. Formatter 설정
- logging.Formatter를 이용해 로그 출력 형식을 지정한다. (여기서는 날짜, 로거명, 로그 레벨, 메시지를 포함)
4. 필터 적용
- ExactLevelFilter를 구현하여 핸들러에 오직 특정 레벨의 로그만 통과시키도록 한다.
- 예를 들어, logfile_view 핸들러에는 ExactLevelFilter(logging.ERROR)를 추가하여 에러 메시지만 기록한다.
5. 핸들러 추가
- 각 로거에 addHandler()를 호출하여 설정한 핸들러들을 추가한다.
import logging
from logging.handlers import TimedRotatingFileHandler
from fastapi import FastAPI
# -------------------------------------------------------------------
# 커스텀 필터 클래스: 특정 레벨의 로그만 통과시키도록 구현
# -------------------------------------------------------------------
class ExactLevelFilter(logging.Filter):
def __init__(self, level):
super().__init__()
self.level = level
def filter(self, record):
# record.levelno가 지정한 레벨과 정확히 일치할 때만 True 리턴
return record.levelno == self.level
# -------------------------------------------------------------------
# 1. console_view: 모든 로그를 콘솔에 출력
# -------------------------------------------------------------------
logger_console = logging.getLogger("console_view")
logger_console.setLevel(logging.DEBUG) # 모든 로그 레벨을 처리하도록 설정
# 콘솔 핸들러 생성 및 포맷터 설정
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG) # 모든 로그 레벨 출력
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
# console_view에 핸들러 추가
logger_console.addHandler(console_handler)
# -------------------------------------------------------------------
# 2. logfile_view: ERROR 레벨 로그만 logfile.txt에 기록 (매일 자정 교체)
# -------------------------------------------------------------------
logger_error_file = logging.getLogger("logfile_view")
logger_error_file.setLevel(logging.ERROR)
# TimedRotatingFileHandler를 사용하여 하루마다 새 파일 생성
error_file_handler = TimedRotatingFileHandler(
filename="logfile.txt", # 로그 파일명
when="midnight", # 매일 자정에 회전
interval=1, # 1일 간격
backupCount=7, # 최근 7일분 로그 파일 유지 (필요에 따라 조정)
encoding="utf-8"
)
error_file_handler.setLevel(logging.ERROR)
# 오직 ERROR 레벨만 기록하도록 필터 적용
error_file_handler.addFilter(ExactLevelFilter(logging.ERROR))
error_file_handler.setFormatter(formatter)
logger_error_file.addHandler(error_file_handler)
# -------------------------------------------------------------------
# 3. logfile_with_console: INFO 레벨 로그를 파일(log_with_console.txt)과 콘솔 모두에 출력
# -------------------------------------------------------------------
logger_info = logging.getLogger("logfile_with_console")
logger_info.setLevel(logging.INFO)
# 파일 핸들러: 하루 단위 회전
info_file_handler = TimedRotatingFileHandler(
filename="log_with_console.txt", # 파일명 지정
when="midnight",
interval=1,
backupCount=7,
encoding="utf-8"
)
info_file_handler.setLevel(logging.INFO)
# 오직 INFO 레벨만 기록하도록 필터 적용
info_file_handler.addFilter(ExactLevelFilter(logging.INFO))
info_file_handler.setFormatter(formatter)
# 콘솔 핸들러: INFO 레벨 로그만 출력
info_console_handler = logging.StreamHandler()
info_console_handler.setLevel(logging.INFO)
info_console_handler.addFilter(ExactLevelFilter(logging.INFO))
info_console_handler.setFormatter(formatter)
# 두 핸들러 모두 logger_info에 추가
logger_info.addHandler(info_file_handler)
logger_info.addHandler(info_console_handler)
# -------------------------------------------------------------------
# FastAPI 애플리케이션 구성
# -------------------------------------------------------------------
app = FastAPI()
@app.get("/")
def read_root():
# 각 로거를 사용하여 로그 메시지 남기기
# console_view: 모든 로그가 콘솔에 출력됨
logger_console.debug("DEBUG: console_view에서 출력되는 디버그 메시지")
logger_console.info("INFO: console_view에서 출력되는 정보 메시지")
logger_console.error("ERROR: console_view에서 출력되는 에러 메시지")
# logfile_view: 오직 ERROR 레벨만 logfile.txt에 기록됨
logger_error_file.error("ERROR: logfile_view에서 출력되는 에러 메시지")
# logfile_with_console: 오직 INFO 레벨만 기록되어, 파일과 콘솔에 출력됨
logger_info.info("INFO: logfile_with_console에서 출력되는 정보 메시지")
return {"message": "로그가 설정되었습니다. 콘솔 및 파일 로그를 확인하세요."}
- reference :
https://docs.python.org/3/howto/logging.html
Logging HOWTO
Author, Vinay Sajip <vinay_sajip at red-dove dot com>,. This page contains tutorial information. For links to reference information and a logging cookbook, please see Other resources. Basic Logging...
docs.python.org
https://docs.python.org/3/library/logging.html
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
'개발언어 Back-End > Python' 카테고리의 다른 글
itertools(반복자 도구) 파이썬 라이브러리 정리 (0) | 2025.02.15 |
---|---|
Python Logging 설정 파일 매뉴얼 (0) | 2025.02.13 |
컴프리헨션 / 제너레이터 정리 / 지연평가 (0) | 2025.02.12 |
파이썬 기반 docker 라이브러리 정리 (0) | 2025.02.09 |
파이썬에서 CPU와 메모리 사용량을 추적하는 방법 (0) | 2024.05.14 |
댓글