로깅(logging)은 소프트웨어 개발에서 매우 중요한 기능입니다. 로깅을 통해 프로그램의 동작을 추적하고, 문제 발생 시 디버깅에 필요한 정보를 수집할 수 있습니다. 파이썬(Python)은 내장 모듈인 logging을 제공하여, 손쉽게 로깅 기능을 구현할 수 있습니다. 이번 포스팅에서는 파이썬의 logging 모듈을 활용하여 효과적으로 로그를 관리하는 방법을 알아보겠습니다.

1. 로깅(logging)이란?

로깅은 프로그램이 실행되는 동안 발생하는 다양한 이벤트나 데이터를 기록하는 것을 의미합니다. 로깅을 통해 개발자는 프로그램의 동작을 추적하고, 오류를 분석하며, 성능을 모니터링할 수 있습니다.

파이썬의 logging 모듈은 다양한 로그 수준, 출력 형식, 로그 핸들러를 제공하여 유연하고 강력한 로깅 시스템을 구축할 수 있도록 도와줍니다.

2. 로깅 모듈의 기본 사용법

파이썬의 logging 모듈을 사용하면 간단한 로깅을 빠르게 구현할 수 있습니다. 가장 기본적인 방법은 logging.basicConfig()를 사용하여 기본 설정을 지정한 후, 다양한 로그 메시지를 출력하는 것입니다.

2.1. 기본적인 로깅 설정

import logging

# 로깅 설정
logging.basicConfig(level=logging.DEBUG)

# 로그 메시지 출력
logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")

위 코드에서는 로그의 기본 설정을 logging.basicConfig()로 지정하고, 각 로그 수준에 맞는 메시지를 출력했습니다. 각 로그는 다음과 같은 로그 수준을 가집니다.

2.2. 로그 수준 (Log Levels)

로그 메시지는 중요도에 따라 다섯 가지의 기본 로그 수준을 가집니다.

  • DEBUG: 상세한 정보, 주로 진단 목적에 사용
  • INFO: 일반적인 정보 메시지
  • WARNING: 경고성 메시지, 문제가 발생할 수 있는 가능성을 경고
  • ERROR: 오류가 발생한 경우, 프로그램 실행에는 영향이 있지만 심각한 수준은 아님
  • CRITICAL: 심각한 오류, 프로그램이 실행을 계속할 수 없는 상태

2.3. 로그 출력 형식 지정

로그 메시지의 출력 형식을 지정하려면 basicConfig()의 format 인수를 사용합니다.

import logging

# 로깅 설정: 로그 메시지의 형식 지정
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s - %(levelname)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)

logging.info("This is an info message with timestamp")

위 코드에서는 로그 메시지에 타임스탬프, 로그 수준, 메시지가 포함되도록 형식을 지정했습니다.

출력 예시는 다음과 같습니다:

2024-08-11 12:00:00 - INFO - This is an info message with timestamp

3. 고급 로깅 설정

파이썬의 logging 모듈은 기본 설정 외에도, 로그를 파일에 저장하거나, 여러 출력 핸들러를 사용하여 복잡한 로깅 요구사항을 충족할 수 있습니다.

3.1. 로그를 파일에 저장하기

로그를 파일에 저장하려면 FileHandler를 사용하여 파일을 지정합니다.

import logging

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

# 파일 핸들러 생성
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)

# 로그 형식 지정
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

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

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

위 코드에서는 로그를 app.log 파일에 저장하도록 설정했습니다. 로그 파일에는 지정한 형식으로 로그 메시지가 기록됩니다.

3.2. 콘솔과 파일에 동시에 로그 출력

여러 핸들러를 사용하여 로그를 동시에 여러 곳에 출력할 수 있습니다. 예를 들어, 콘솔과 파일에 로그를 동시에 출력하려면 StreamHandler와 FileHandler를 함께 사용할 수 있습니다.

import logging

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

# 콘솔 핸들러
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 파일 핸들러
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)

# 로그 형식 지정
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

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

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

위 코드에서는 로그가 콘솔과 app.log 파일에 동시에 출력됩니다.

3.3. 로그 회전 (Rotating Logs)

로그 파일이 너무 커지는 것을 방지하기 위해, 로그 파일을 일정 크기 이상이 되면 새로운 파일로 회전(rotating)할 수 있습니다. 이를 위해 RotatingFileHandler를 사용할 수 있습니다.

import logging
from logging.handlers import RotatingFileHandler

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

# 로그 회전 핸들러 설정 (파일 크기가 1MB를 초과하면 새로운 파일 생성, 백업 파일은 5개까지 유지)
handler = RotatingFileHandler('app.log', maxBytes=1e6, backupCount=5)
handler.setLevel(logging.DEBUG)

# 로그 형식 지정
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

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

# 로그 메시지 출력
for i in range(10000):
    logger.debug(f"Log message {i}")

위 코드에서는 로그 파일이 1MB를 초과하면 새로운 로그 파일을 생성하고, 최대 5개의 백업 파일을 유지하도록 설정했습니다.

4. 로깅 활용 사례

4.1. 로깅을 통한 디버깅

로깅은 디버깅 목적으로 매우 유용합니다. 개발 중에 발생할 수 있는 오류나 경고를 로깅하여, 문제 발생 시 원인을 추적할 수 있습니다.

import logging

logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")

def divide(a, b):
    logging.debug(f"Dividing {a} by {b}")
    try:
        result = a / b
    except ZeroDivisionError:
        logging.error("Attempted to divide by zero")
        return None
    else:
        logging.info(f"Result: {result}")
        return result

divide(10, 2)
divide(10, 0)

4.2. 성능 측정 및 모니터링

로깅을 사용하여 성능 측정이나 애플리케이션 모니터링을 수행할 수 있습니다. 예를 들어, 함수의 실행 시간을 로깅하여 성능 병목 지점을 식별할 수 있습니다.

import logging
import time

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")

def time_it(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        logging.info(f"Execution time for {func.__name__}: {end_time - start_time} seconds")
        return result
    return wrapper

@time_it
def slow_function():
    time.sleep(2)
    return "Finished"

slow_function()

5. 로거 계층 구조

파이썬의 로깅 시스템은 계층 구조를 가지며, 상위 로거의 설정이 하위 로거에 상속됩니다. 예를 들어, my_app.module 로거는 my_app 로거의 설정을 상속받습니다. 이를 통해 프로젝트의 특정 모듈이나 패키지에 대해 별도의 로깅 설정을 적용할 수 있습니다.

import logging

# 상위 로거 설정
parent_logger = logging.getLogger('my_app')
parent_logger.setLevel(logging.INFO)

# 하위 로거 설정
child_logger = logging.getLogger('my_app.module')
child_logger.set

Level(logging.DEBUG)

# 상위 로거에서 로그 출력
parent_logger.info("This is an info message from the parent logger")
child_logger.debug("This is a debug message from the child logger")

결론

이번 포스팅에서는 파이썬의 로깅(logging) 모듈을 활용하는 방법에 대해 알아보았습니다. 로깅을 통해 프로그램의 동작을 추적하고, 디버깅과 모니터링을 쉽게 할 수 있으며, 성능 최적화와 문제 해결에 중요한 역할을 합니다. 기본적인 로깅 설정부터 고급 로그 파일 회전, 여러 핸들러를 사용하는 방법까지 다양한 로깅 기능을 실습해 보면서, 로깅 시스템을 프로젝트에 효과적으로 적용해 보세요.


이 글을 통해 파이썬의 로깅 기능을 이해하고, 실습을 통해 이를 사용하는 방법을 익힐 수 있을 것입니다. 로깅을 활용해 더 안정적이고 유지보수하기 쉬운 파이썬 애플리케이션을 만들어 보세요!

+ Recent posts