logging.info("철수님 로그인") → app.log에 시간까지 자동 기록! 😊
print()는 화면에만 보이지만, logging은 파일로 영구 저장! “2025-04-16 10:30:15 [ERROR] 결제 실패” 처럼 시간, 레벨, 메시지 모두 기록합니다!
(40-50분 완독 ⭐⭐⭐)
🎯 오늘의 학습 목표
📚 사전 지식
🎯 학습 목표 1: 로깅의 개념과 필요성 이해하기
1.1 로깅이란?
로깅(Logging): 프로그램 실행 중 발생하는 이벤트를 기록하는 것
1
2
3
| # print() vs logging
print("사용자 로그인") # 화면에만 출력, 프로그램 종료 시 사라짐
logging.info("사용자 로그인") # 파일에 영구 저장 + 시간/레벨 자동 기록
|
1.2 print() vs logging
| 구분 | print() | logging |
| 저장 | 화면에만 출력 | 파일로 영구 저장 가능 |
| 시간 기록 | 수동으로 추가 | 자동 기록 |
| 레벨 구분 | 없음 | DEBUG, INFO, WARNING, ERROR, CRITICAL |
| 운영 환경 | 사용 어려움 | 필수 |
| 필터링 | 불가능 | 레벨별 필터링 가능 |
1.3 로깅이 필요한 상황
1
2
3
4
5
6
7
8
9
10
11
12
13
| # ❌ print로는 부족한 상황
def process_order(order_id):
print(f"주문 처리 시작: {order_id}") # 서버 재시작하면 사라짐!
# ...
print("주문 처리 완료")
# ✅ logging 사용
import logging
def process_order(order_id):
logging.info(f"주문 처리 시작: {order_id}") # 파일에 영구 저장!
# ...
logging.info("주문 처리 완료")
|
로깅이 필수인 경우:
- 서버 애플리케이션 (24시간 실행)
- 오류 추적이 필요한 경우
- 사용자 행동 분석
- 보안 감사 로그
🎯 학습 목표 2: logging 모듈 기초 사용법 배우기
2.1 기본 로깅
1
2
3
4
5
6
7
8
9
10
11
| import logging
# 기본 설정 - INFO 레벨 이상만 출력
logging.basicConfig(level=logging.INFO)
# 로그 출력
logging.debug("디버그 메시지") # 출력 안됨 (INFO보다 낮음)
logging.info("정보 메시지") # INFO:root:정보 메시지
logging.warning("경고 메시지") # WARNING:root:경고 메시지
logging.error("오류 메시지") # ERROR:root:오류 메시지
logging.critical("치명적 오류") # CRITICAL:root:치명적 오류
|
💡 level=logging.INFO로 설정하면 INFO 이상(INFO, WARNING, ERROR, CRITICAL)만 출력됩니다. DEBUG는 출력되지 않습니다.
2.2 로그 레벨
| 레벨 | 숫자 | 용도 |
| DEBUG | 10 | 상세한 디버그 정보 (개발 중에만 사용) |
| INFO | 20 | 일반 정보 (프로그램 시작/종료, 주요 이벤트) |
| WARNING | 30 | 경고 (문제가 될 수 있는 상황, 프로그램은 계속 실행) |
| ERROR | 40 | 오류 (기능이 실패했지만 프로그램은 계속 실행) |
| CRITICAL | 50 | 치명적 오류 (프로그램 중단이 필요한 심각한 오류) |
1
2
3
4
5
6
| # 레벨별 사용 예시
logging.debug("사용자 입력값: {'name': 'Alice', 'age': 25}") # 개발 디버깅용
logging.info("서버가 포트 8080에서 시작되었습니다") # 일반 정보
logging.warning("디스크 용량이 10% 미만입니다") # 경고
logging.error("데이터베이스 연결 실패") # 오류
logging.critical("시스템 메모리 부족으로 종료합니다") # 치명적
|
🎯 학습 목표 3: 로그 레벨과 포맷 설정하기
3.1 포맷 지정
1
2
3
4
5
6
7
8
| logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info("애플리케이션 시작")
# 2025-04-16 10:00:00 - root - INFO - 애플리케이션 시작
|
3.2 주요 포맷 속성
1
2
3
4
5
6
7
| # %(asctime)s - 시간
# %(name)s - 로거 이름
# %(levelname)s - 로그 레벨
# %(message)s - 메시지
# %(filename)s - 파일명
# %(lineno)d - 줄 번호
# %(funcName)s - 함수명
|
🎯 학습 목표 4: 파일 로거와 핸들러 활용하기
💡 핸들러(Handler)란? 로그를 “어디에” 출력할지 결정하는 객체입니다.
FileHandler: 파일에 저장 StreamHandler: 화면(콘솔)에 출력 RotatingFileHandler: 파일에 저장하되, 크기가 커지면 자동으로 새 파일 생성
4.1 파일에 저장
1
2
3
4
5
6
7
8
| logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='app.log',
filemode='a' # 'w'는 덮어쓰기, 'a'는 추가
)
logging.info("파일에 기록됨")
|
4.2 파일과 콘솔에 동시 출력
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| import logging
# 로거 생성
# __name__은 현재 모듈 이름입니다 (예: 'main', 'myapp.utils')
# 이렇게 하면 어느 모듈에서 로그가 발생했는지 알 수 있습니다
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# 포맷터 - 로그 출력 형식을 지정합니다
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 출력 예: 2025-04-16 10:30:15 - __main__ - INFO - 서버 시작
# 파일 핸들러 - INFO 이상만 파일에 저장
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.INFO) # INFO, WARNING, ERROR, CRITICAL만 파일에
file_handler.setFormatter(formatter)
# 콘솔 핸들러 - DEBUG 이상 모두 화면에 출력
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG) # 모든 레벨 화면에 출력
console_handler.setFormatter(formatter)
# 로거에 핸들러 추가
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 사용
logger.debug("디버그 (콘솔만)") # 화면에만 출력 (파일 X)
logger.info("정보 (파일+콘솔)") # 화면 + 파일 둘 다
logger.error("오류 (파일+콘솔)") # 화면 + 파일 둘 다
|
💡 핸들러마다 레벨을 다르게 설정할 수 있습니다. 개발 중에는 콘솔에서 DEBUG까지 보고, 파일에는 INFO 이상만 저장하는 것이 일반적입니다.
4.3 로그 로테이션 (크기 기반)
💡 로그 로테이션이란? 로그 파일이 계속 커지면 디스크가 가득 찹니다. 로테이션은 파일 크기가 일정 이상이 되면 자동으로 새 파일을 만들고, 오래된 파일은 삭제합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| from logging.handlers import RotatingFileHandler
logger = logging.getLogger(__name__)
# 10MB마다 새 파일, 최대 5개 백업
# app.log → app.log.1 → app.log.2 → ... → app.log.5 (가장 오래된 건 삭제)
handler = RotatingFileHandler(
'app.log',
maxBytes=10*1024*1024, # 10MB
backupCount=5 # 최대 5개 파일 유지
)
formatter = logging.Formatter('%(asctime)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
|
4.4 로그 로테이션 (시간 기반)
1
2
3
4
5
6
7
8
9
| from logging.handlers import TimedRotatingFileHandler
# 매일 자정에 새 파일
handler = TimedRotatingFileHandler(
'app.log',
when='midnight',
interval=1,
backupCount=30 # 30일 보관
)
|
4.5 실전 로깅 설정 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| def setup_logging(log_file='app.log', level=logging.INFO):
"""로깅 설정"""
logger = logging.getLogger()
logger.setLevel(level)
# 포맷
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 파일 핸들러 (로테이션)
file_handler = RotatingFileHandler(
log_file,
maxBytes=10*1024*1024,
backupCount=5
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# 콘솔 핸들러
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# 사용
setup_logging('myapp.log', level=logging.DEBUG)
|
4.6 모듈별 로거
1
2
3
4
5
6
7
8
9
10
11
12
13
| # module1.py
import logging
logger = logging.getLogger(__name__)
def function1():
logger.info("module1.function1 실행")
# module2.py
import logging
logger = logging.getLogger(__name__)
def function2():
logger.info("module2.function2 실행")
|
4.7 예외 발생 시 로깅
💡 예외 로깅이 중요한 이유: 프로그램이 오류로 멈추면 “왜 멈췄는지” 알아야 합니다. logging.exception()은 오류 메시지와 함께 전체 에러 추적 정보(traceback)를 자동으로 기록합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def divide(a, b):
try:
result = a / b
logging.info(f"{a} / {b} = {result}")
return result
except ZeroDivisionError:
# exception()은 ERROR 레벨 + traceback 자동 포함
logging.exception("0으로 나누기 오류 발생!")
return None
# 사용
divide(10, 2) # 정상
divide(10, 0) # 오류 발생
|
출력 결과:
1
2
3
4
5
6
| 2025-04-16 10:00:00 - INFO - 10 / 2 = 5.0
2025-04-16 10:00:01 - ERROR - 0으로 나누기 오류 발생!
Traceback (most recent call last):
File "example.py", line 9, in divide
result = a / b
ZeroDivisionError: division by zero
|
💡 logging.exception() vs logging.error()
logging.error("메시지"): 오류 메시지만 기록 logging.exception("메시지"): 오류 메시지 + traceback 전체 기록
예외 처리 블록(except) 안에서는 exception()을 사용하세요!
4.8 JSON 형식 로깅
💡 JSON 로그가 필요한 이유: 일반 텍스트 로그는 사람이 읽기 좋지만, 로그 분석 도구(Elasticsearch, Splunk 등)로 자동 분석하려면 JSON 형식이 편리합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| import logging
import json
from datetime import datetime
class JSONFormatter(logging.Formatter):
"""로그를 JSON 형식으로 변환하는 포맷터"""
def format(self, record):
log_data = {
'timestamp': datetime.now().isoformat(),
'level': record.levelname,
'logger': record.name,
'message': record.getMessage(),
'file': record.filename,
'line': record.lineno
}
# 예외 정보가 있으면 추가
if record.exc_info:
log_data['exception'] = self.formatException(record.exc_info)
return json.dumps(log_data, ensure_ascii=False)
# 사용
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# JSON 포맷터 적용
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)
logger.info("서버 시작")
logger.warning("메모리 사용량 높음")
|
출력 결과:
1
2
| {"timestamp": "2025-04-16T10:00:00", "level": "INFO", "logger": "__main__", "message": "서버 시작", "file": "example.py", "line": 25}
{"timestamp": "2025-04-16T10:00:01", "level": "WARNING", "logger": "__main__", "message": "메모리 사용량 높음", "file": "example.py", "line": 26}
|
🎯 학습 목표 5: 실전 로깅 패턴 익히기
5.1 사용자 활동 로깅 (웹 서비스)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| import logging
from datetime import datetime
# 로거 설정
logger = logging.getLogger('user_activity')
logger.setLevel(logging.INFO)
handler = logging.FileHandler('user_activity.log')
handler.setFormatter(logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s'
))
logger.addHandler(handler)
def log_user_action(user_id, action, details=None):
"""사용자 활동 기록"""
message = f"[User:{user_id}] {action}"
if details:
message += f" | {details}"
logger.info(message)
def log_user_error(user_id, action, error):
"""사용자 관련 오류 기록"""
logger.error(f"[User:{user_id}] {action} 실패 | Error: {error}")
# 사용 예시
log_user_action("user123", "로그인", "IP: 192.168.1.100")
log_user_action("user123", "상품 조회", "상품ID: PROD001")
log_user_action("user123", "장바구니 추가", "상품ID: PROD001, 수량: 2")
log_user_error("user123", "결제", "카드 인증 실패")
log_user_action("user123", "로그아웃")
|
user_activity.log 파일 내용:
1
2
3
4
5
| 2025-04-16 10:00:00 - INFO - [User:user123] 로그인 | IP: 192.168.1.100
2025-04-16 10:00:05 - INFO - [User:user123] 상품 조회 | 상품ID: PROD001
2025-04-16 10:00:10 - INFO - [User:user123] 장바구니 추가 | 상품ID: PROD001, 수량: 2
2025-04-16 10:00:15 - ERROR - [User:user123] 결제 실패 | Error: 카드 인증 실패
2025-04-16 10:00:20 - INFO - [User:user123] 로그아웃
|
5.2 API 요청/응답 로깅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| import logging
import time
logger = logging.getLogger('api')
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s'
))
logger.addHandler(handler)
def api_request(endpoint, method="GET", data=None):
"""API 요청 처리 (시뮬레이션)"""
request_id = f"REQ-{int(time.time())}"
# 요청 로깅
logger.info(f"[{request_id}] {method} {endpoint} 요청 시작")
if data:
logger.debug(f"[{request_id}] 요청 데이터: {data}")
start_time = time.time()
try:
# 실제로는 여기서 API 호출
time.sleep(0.1) # 처리 시간 시뮬레이션
# 응답 성공
elapsed = round((time.time() - start_time) * 1000, 2)
logger.info(f"[{request_id}] {method} {endpoint} 완료 ({elapsed}ms)")
return {"status": "success"}
except Exception as e:
logger.exception(f"[{request_id}] {method} {endpoint} 실패")
raise
# 사용
api_request("/api/users", "GET")
api_request("/api/orders", "POST", {"product": "A001", "qty": 2})
|
출력 결과:
1
2
3
4
5
| 2025-04-16 10:00:00 - INFO - [REQ-1713225600] GET /api/users 요청 시작
2025-04-16 10:00:00 - INFO - [REQ-1713225600] GET /api/users 완료 (102.5ms)
2025-04-16 10:00:00 - INFO - [REQ-1713225601] POST /api/orders 요청 시작
2025-04-16 10:00:00 - DEBUG - [REQ-1713225601] 요청 데이터: {'product': 'A001', 'qty': 2}
2025-04-16 10:00:00 - INFO - [REQ-1713225601] POST /api/orders 완료 (101.3ms)
|
5.3 애플리케이션 시작/종료 로깅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| import logging
import atexit
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='app.log'
)
def startup():
"""애플리케이션 시작"""
logging.info("=" * 50)
logging.info("애플리케이션 시작")
logging.info(f"Python 버전: 3.x")
logging.info(f"설정 파일: config.yml")
logging.info("=" * 50)
def shutdown():
"""애플리케이션 종료"""
logging.info("=" * 50)
logging.info("애플리케이션 정상 종료")
logging.info("=" * 50)
# 종료 시 자동 호출
atexit.register(shutdown)
# 프로그램 시작
startup()
logging.info("메인 로직 실행 중...")
# ... 프로그램 로직 ...
|
app.log 파일 내용:
1
2
3
4
5
6
7
8
9
| 2025-04-16 10:00:00 - INFO - ==================================================
2025-04-16 10:00:00 - INFO - 애플리케이션 시작
2025-04-16 10:00:00 - INFO - Python 버전: 3.x
2025-04-16 10:00:00 - INFO - 설정 파일: config.yml
2025-04-16 10:00:00 - INFO - ==================================================
2025-04-16 10:00:01 - INFO - 메인 로직 실행 중...
2025-04-16 10:00:05 - INFO - ==================================================
2025-04-16 10:00:05 - INFO - 애플리케이션 정상 종료
2025-04-16 10:00:05 - INFO - ==================================================
|
💡 실전 팁 & 주의사항
✅ 로깅 베스트 프랙티스
- 모듈별 로거 사용
1
2
3
| # 각 모듈마다
import logging
logger = logging.getLogger(__name__)
|
- 적절한 로그 레벨 선택
- DEBUG: 개발 중 상세 정보
- INFO: 일반 정보 (시작/종료)
- WARNING: 경고 (프로그램은 계속 실행)
- ERROR: 오류 (기능 실패)
- CRITICAL: 치명적 오류 (프로그램 중단)
- 민감 정보 주의
1
2
3
4
5
| # ❌ 나쁜 예
logger.info(f"Password: {password}")
# ✅ 좋은 예
logger.info("User login attempt")
|
⚠️ 주의사항
- 로그 파일 크기 관리 (로테이션 필수)
- 성능에 영향을 줄 수 있으니 과도한 로깅 지양
- 운영 환경에서는 INFO 이상 레벨만 기록
🧪 연습 문제
문제 1: 모듈별 로거 설정
목표: 여러 모듈에서 사용할 수 있는 로깅 시스템을 구성하세요.
요구사항:
- 파일과 콘솔에 동시 출력
- 파일은 INFO 이상, 콘솔은 WARNING 이상
- 로그 로테이션 적용 (10MB, 3개 백업)
해답 보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| import logging
from logging.handlers import RotatingFileHandler
def setup_logger(name, log_file='app.log'):
"""로거 설정"""
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
# 포맷
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 파일 핸들러 (INFO 이상, 로테이션)
file_handler = RotatingFileHandler(
log_file,
maxBytes=10*1024*1024, # 10MB
backupCount=3
)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
# 콘솔 핸들러 (WARNING 이상)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)
console_handler.setFormatter(formatter)
# 핸들러 추가
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger
# 사용
logger = setup_logger('my_module')
logger.debug("디버그 (파일X, 콘솔X)")
logger.info("정보 (파일O, 콘솔X)")
logger.warning("경고 (파일O, 콘솔O)")
|
문제 2: 간단한 로깅 시스템
목표: 아래 요구사항에 맞는 로깅을 설정하세요.
요구사항:
my_app.log 파일에 로그 저장 - 포맷:
시간 - 레벨 - 메시지 - INFO 레벨 이상만 기록
해답 보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| import logging
# 기본 설정으로 간단하게!
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='my_app.log',
filemode='a' # 추가 모드 (기존 내용 유지)
)
# 사용
logging.debug("이건 기록 안됨") # INFO보다 낮음
logging.info("프로그램 시작") # 기록됨
logging.warning("주의!") # 기록됨
logging.error("오류 발생!") # 기록됨
# my_app.log 파일 내용:
# 2025-04-16 10:00:00,123 - INFO - 프로그램 시작
# 2025-04-16 10:00:00,124 - WARNING - 주의!
# 2025-04-16 10:00:00,125 - ERROR - 오류 발생!
|
📝 오늘 배운 내용 정리
| 개념 | 설명 | 예시 |
| 로그 레벨 | 로그의 중요도 | DEBUG < INFO < WARNING < ERROR < CRITICAL |
| 핸들러 | 로그 출력 대상 | FileHandler, StreamHandler, RotatingFileHandler |
| 포맷터 | 로그 출력 형식 | '%(asctime)s - %(levelname)s - %(message)s' |
| 로거 | 로그 기록 객체 | logger = logging.getLogger(__name__) |
| 로그 로테이션 | 자동 파일 관리 | RotatingFileHandler, TimedRotatingFileHandler |
| 예외 로깅 | 오류 + traceback 기록 | logging.exception("오류 메시지") |
| JSON 로깅 | 구조화된 로그 | 커스텀 JSONFormatter 클래스 |
핵심 코드 패턴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| import logging
from logging.handlers import RotatingFileHandler
# 로거 생성
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# 포맷터
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 파일 핸들러 (로테이션)
file_handler = RotatingFileHandler(
'app.log', maxBytes=10*1024*1024, backupCount=5
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# 로그 기록
logger.info("애플리케이션 시작")
logger.error("오류 발생")
|
🔗 관련 자료
📚 이전 학습
Day 46: 예외 처리 고급 ⭐⭐⭐
어제는 커스텀 예외, 예외 체이닝, 컨텍스트 매니저 등 고급 예외 처리를 배웠습니다!
📚 다음 학습
Day 48: 디버깅 기법 ⭐⭐⭐
내일은 print 디버깅부터 pdb 디버거까지, 효율적으로 버그를 찾고 수정하는 방법을 배웁니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
| Day 47/100 | Phase 5: 파일 처리와 예외 처리 | #100DaysOfPython |
이제와서 시작하는 Python 마스터하기 - Day 47 완료! 🎉