Profile picture

[Python] 파이썬 로깅 (logging)

JaehyoJJAng2024년 05월 10일

logging

logging은 디버깅 및 오류 해결, 코드 유지 보수, 시스템 모니터링을 돕기 위해 중요한 역할을 한다.

로깅을 도입해야하는 주요 이유들은 다음과 같다.


1. 문제 해결 및 디버깅

logging은 프로그램 실행 중 발생하는 이벤트와 상태를 기록하여 문제 발생 시 빠른 원인 파악이 가능하다.

예를 들어, 코드의 특정 부분에 오류가 발생하거나 예외 처리가 발동될 때 그에 대한 상세한 로그가 남아 있으면 문제 해결이 용이해진다.


2. 운영 환경에서의 모니터링

print()와 같은 방법으로는 로그를 효과적으로 관리하기가 매우 어렵다.

반면 logging 모듈은 로그 메시지를 다양한 수준(예: DEBUG, INFO, WARNING, ERROR, CRITICAL)으로 분류하고,

파일, 콘솔, 원격 서버 등 다양한 대상에 로그를 출력할 수 있게 해준다.

이를 통해 운영 환경에서 시스템을 모니터링하고 경고 또는 오류 발생 시 신속한 대처가 가능해진다.


3. 가독성 및 관리 용이

logging 모듈은 로그 메시지의 형식을 사용자가 지정할 수도 있고, 시간, 모듈 이름, 라인 번호 등과 같은 추가 정보를 포함할 수 있어

로그의 가독성이 향상된다.

이는 유지보수에 매우 유리하다.


Getting Started

기본적인 로깅 사용

기본적으로 logging 모듈을 사용하여 메시지를 출력할 수 있다.

logging 모듈은 다섯 가지의 로그 수준을 제공한다: DEBUG, INFO, WARNING, ERROR, CRITICAL

각 수준은 메시지의 중요도를 나타낸다.

import logging

# 기본 설정: 로그 레벨은 WARNING, 로그 형식은 기본 값
logging.basicConfig()

# 로그 메시지 출력
logging.debug("This is a debug message") # DEBUG 레벨의 메시지 (출력되지 않음)
logging.info("This is a info message") # INFO 레벨의 메시지 (출력되지 않음)
logging.warnig("This is a warning message") # WARNING 레벨의 메시지 (출력됨)
logging.error("This is an error message") # ERROR 레벨의 메시지 (출력됨)
logging.critical("This is a critical message")  # CRITICAL 레벨의 메시지 (출력됨)

로그 형식과 레벨 설정

로그 형식(format)을 지정하고, 로그 레벨을 DEBUG로 설정하여 더 많은 정보를 출력하도록 할 수 있다.

import logging

# 로그 설정: 레벨은 DEBUG, 형식은 시간, 레벨, 메시지
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s')

# 로그 메시지 출력
logging.debug("Debugging information")
logging.info("Informational message")
logging.warning("Warning: Something might go wrong")
logging.error("Error occurred")
logging.critical("Critical issue")
  • format='%(asctime)s - %(levelname)s - %(message)s': 로그 메시지 형식을 지정한다.
    • 여기서 %(asctime)s는 시간, %(levelname)s는 로그 수준, %(message)s는 로그 메시지를 나타낸다.

로그를 파일로 저장

로그를 콘솔뿐만 아니라 파일로도 저장할 수 있다. 이를 통해 로그 데이터를 영구적으로 보관하거나 분석할 수 있다.

import logging

# 로그 설정: 로그 파일에 기록, 레벨은 INFO 이상, 형식 지정
logging.basicConfig(filename='app.log', level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

logging.info("This message will be logged to the file")
logging.error("An error has occurred")
  • filename='app.log': 로그가 기록될 파일의 이름을 지정한다.
    • 이 예제에서는 app.log 파일에 로그가 저장된다.

여러 핸들러 사용하기 (콘솔과 파일 동시 기록)

하나의 로거(logger)로 여러 핸들러(handler)를 설정하여, 로그를 콘솔과 파일에 동시 기록할 수 있다.

import logging

# 로거 생성
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

# 콘솔 핸들러 생성 및 설정
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)

# 파일 핸들러 생성 및 설정
file_handler = logging.FileHandler('combined.log')
file_handler.setLevel(logging.ERROR)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 핸들러를 로거에 추가
logger.addHandler(console_handler)
logger.addHandler(file_handler)

# 로그 메시지 출력
logger.debug("This is a debug message")  # 콘솔에만 출력됨
logger.error("This is an error message")  # 콘솔과 파일 모두에 기록됨
  • 로거(logger): logging.getLogger()로 새로운 로거를 생성한다.
    • my_logger라는 이름을 사용한다.
  • 핸들러(handler): StreamHandler()는 콘솔로, FileHandler()는 파일로 로그를 출력한다.
  • 포매터(formatter): 핸들러마다 다른 형식의 로그 출력을 위해 Formatter()를 사용한다.
  • 콘솔에는 모든 수준의 로그가 출력되지만, 파일에는 ERROR 이상 레벨의 로그만 기록된다.

로테이팅 파일 핸들러 사용

로그 파일이 일정 크기에 도달하면 새 파일로 교체되도록 설정할 수 있다.

이를 통해 로그 파일이 너무 커지지 않게 관리할 수 있다.

import logging
from logging.handlers import RotatingFileHandler

# 로테이팅 파일 핸들러 설정
handler = RotatingFileHandler('rotating.log', maxBytes=2000, backupCount=5)
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

logger = logging.getLogger('rotating_logger')
logger.setLevel(logging.INFO)
logger.addHandler(handler)

# 로그 메시지 출력
for i in range(100):
    logger.info(f"This is log message {i}")
  • RotatingFileHandler: maxBytes가 설정한 바이트 크기에 도달하면 새 파일을 생성한다.
    • backupCount는 보관할 파일의 최대 개수를 지정한다.
  • 로그 파일이 rotating.log, rotating.log.1, rotating.log.2 등으로 순차적으로 생성된다.

사용자 정의 로그 수준 및 필터 사용

필터를 사용하여 특정 조건에 맞는 로그만 출력할 수 있다.

예를 들어, 특정 모듈에서 발생한 로그만 출력하도록 필터를 만들 수 있다.

import logging

# 사용자 정의 필터 생성
class MyFilter(logging.Filter):
    def filter(self, record):
        return 'specific' in record.getMessage()

# 로거 설정
logger = logging.getLogger('filtered_logger')
logger.setLevel(logging.DEBUG)

# 콘솔 핸들러와 필터 추가
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
console_handler.addFilter(MyFilter())
logger.addHandler(console_handler)

# 로그 메시지 출력
logger.info("This is a general message")
logger.info("This is a specific message")

  • 필터(filter): MyFilter 클래스는 filter 메서드를 재정의하여 메시지에 'specific'이라는 단어가 포함된 경우에만 통과하도록 한다.
  • 핸들러에 필터 추가: console_handler.addFilter(MyFilter())로 핸들러에 필터를 추가한다.
  • 'This is a specific message'만 출력되고, 'This is a general message'는 필터링된다.

실생활 예제

1. 웹 애플리케이션 로깅

웹 애플리케이션은 서버 측에서 많은 사용자의 요청을 처리하고, 다양한 데이터베이스와 상호 작용하며, 다양한 오류가 발생할 수 있다.

이런 환경에서는 각 요청에 대한 정보를 기록하고, 오류 발생 시 문제를 빠르게 식별하는 것이 매우 중요하다.

import logging
from flask import Flask, request

# 플라스크 웹 애플리케이션 생성
app = Flask(__name__)

# 로그 설정
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

@app.route('/')
def index():
    app.logger.info("Index page accessed")
    return "Welcome to the index page!"

@app.route('/user/<username>')
def user(username):
    app.logger.info(f"User page accessed for: {username}")
    return f"Hello, {username}!"

@app.errorhandler(404)
def page_not_found(e):
    app.logger.warning(f"Page not found: {request.url}")
    return "This page does not exist.", 404

@app.errorhandler(500)
def internal_error(e):
    app.logger.error(f"Server error: {request.url}")
    return "Internal server error.", 500

if __name__ == '__main__':
    app.run(debug=True)

  • 요청 로깅: 각 엔드포인트(/, /user/<username>)에 접근할 때마다 로거가 로그를 남긴다.
    • 이는 웹 서버의 액세스 로그처럼 작동하여 누가 언제 어떤 페이지를 방문했는지 기록할 것이다.
  • 오류 로깅: 404500오류에 대한 핸들러가 있으며, 발생 시 경고 또는 오류 로그를 기록한다.
    • 이는 문제를 신속하게 진단하는 데 도움이 된다.
  • 로그 메시지 포맷: 각 로그에는 타임스탬프와 로그 수준이 포함되어 있어, 로그를 분석할 때 시간과 심각도를 쉽게 파악할 수 있다.

Loading script...