본문 바로가기
Python

Python - Log Decorator 활용

by 올엠 2024. 3. 25.
반응형

이글을 통해 Python에서 제공하는 중요 기능중 하나인 Decorator를 활용한 Log Decorator관련해서 정리해보고자 한다.

Decorator 활용이 Log에 적절한 이유는

Decorator는 Python 설명을 보면 잘 나와 있듯이 함수를 감싸는 기능을 제공하기 때문에 실행전, 실행후에 일관성있게 정의하여 적용하고자 할때 Decorator만 한 것이 없다.

즉 log 입장으로 보면, 함수 실행전과 실행후에 대한 로그를 한번에 구성할 수 있어서 매우 유용하다고 할 수 있겠다.

그리고 개발한 프로그램을 서비스할 때 문제 분석을 위해 로그를 기록해야 하는 경우 log를 위한 코드를 추가를 해야하기 때문에 Decorator를 활용하기가 제격이라고 할 수 있다.

가장 간단하게 만들수 있는 log Decorator는 다음과 같다.

def log(func):

    def wrap_log(*args, **kwargs):
        name = func.__name__
        result = func(*args, **kwargs) # 실제 함수를 호출하는 구분
        print('log decorator',name, result)
        return func
 
    return wrap_log


@log
def sum(a):
    return a+a

if __name__ == "__main__":
    value = sum(2)

즉 실행 함수를 func로 가지고 와서 이를 실행하고 실행 결과를 반환해 주게 된다.



이를 활용하여 로깅을 실행전, 실행후로 정의할 수 있고, try/except를 활용하여 문제의 원인을 기록해 쉽게 관리가 가능하다.

Github을  찾아보던중 아래와 같이 로그 데코레이터를 구현해 놓은 소스가 있어 코드는 백업 목적으로 아래 남겨둔다.

log.py

import logging
import os



class CustomFormatter(logging.Formatter):
    """ Custom Formatter does these 2 things:
    1. Overrides 'funcName' with the value of 'func_name_override', if it exists.
    2. Overrides 'filename' with the value of 'file_name_override', if it exists.
    """

    def format(self, record):
        if hasattr(record, 'func_name_override'):
            record.funcName = record.func_name_override
        if hasattr(record, 'file_name_override'):
            record.filename = record.file_name_override
        return super(CustomFormatter, self).format(record)





def get_logger(log_file_name, log_sub_dir=""):
    """ Creates a Log File and returns Logger object """

    windows_log_dir = 'c:\\logs_dir\\'
    linux_log_dir = '/logs_dir/'

    # Build Log file directory, based on the OS and supplied input
    log_dir = windows_log_dir if os.name == 'nt' else linux_log_dir
    log_dir = os.path.join(log_dir, log_sub_dir)

    # Create Log file directory if not exists
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)



    # Build Log File Full Path
    logPath = log_file_name if os.path.exists(log_file_name) else os.path.join(log_dir, (str(log_file_name) + '.log'))

    # Create logger object and set the format for logging and other attributes
    logger = logging.Logger(log_file_name)
    logger.setLevel(logging.DEBUG)
    handler = logging.FileHandler(logPath, 'a+')
    """ Set the formatter of 'CustomFormatter' type as we need to log base function name and base file name """
    handler.setFormatter(CustomFormatter('%(asctime)s - %(levelname)-10s - %(filename)s - %(funcName)s - %(message)s'))
    logger.addHandler(handler)

    # Return logger object
    return logger
반응형