본문 바로가기
Python/업비트 자동매매

(Python) 업비트 자동 매매 프로그램 만들기(6) - 로그 출력

by 미니몬 2023. 11. 9.

목차

    728x90
    반응형

    Summary

    이번 게시물에서는 파이썬의 logging 모듈의 기본적인 사용법과 주요 기능을 살펴보겠습니다.

    파이썬에서 로그를 출력하고 관리하는 것은 어플리케이션 개발의 핵심 요소 중 하나입니다. 

    logging 모듈은 파이썬 내장된 기능으로 로깅 기능을 쉽게 사용할 수 있게 해줍니다. 

    이를 활용한 임의의 'log()' 기능과 'Decorator' 기능도 구현해 보겠습니다. 

     

     

    1) 기본 로깅 시작하기

    • 필요한 모듈 import 
    import os
    import logging
    from logging import handlers
    from datetime import datetime

     - os : 파일 경로를 조작하거나 환경 변수를 접근하는 등의 운영체제와 관련된 기능을 제공합니다.
     - logging : 파이썬의 기본 로깅 시스템으로, 애플리케이션의 실행 상황을 추적할 수 있게 해주는 모듈입니다.
     - handlers : logging 모듈의 일부로, 로그 메시지를 다양한 방식으로 처리할 수 있는 핸들러들을 포함합니다.
     - datetime : 날짜와 시간을 다루기 위한 모듈입니다.

     

    • 필요한 정보 세팅
    """ Logging Configuration """
    LOG_PATH = "D:\\Path\\to\\Logging"  # WINDOWS
    # LOG_PATH = "/Path/to/Logging"  # LINUX
    LOG_FILE = "upbit_bot"  # FILE NAME
    
    log_formatter = logging.Formatter('%(message)s')
    log_handler = handlers.TimedRotatingFileHandler(
        filename=os.path.join(LOG_PATH, LOG_FILE),
        when='midnight',
        interval=1,
        encoding='utf-8'
    )
    log_handler.setFormatter(log_formatter)
    log_handler.suffix = "%Y%m%d"

     - 로그 파일의 경로('LOG_PATH') 와 로그 파일명('LOG_FILE')을 설정합니다.

     - 'TimedRotatingFileHandler' 를 사용하여 로그를 날짜별 파일에 기록 하도록 설정합니다

     - 이 핸들러는 매일 자정('midnight')에 새로운 파일을 생성하고, 파일명에 날짜를 붙입니다 ('suffix')

     - 로그 메시지의 형식을 설정하기 위해 'Formatter'를 사용합니다.

     

    'LOGPATH' 를 지정할 때 윈도우와 리눅스의 입력 방식에 차이가 있습니다.

    본인의 디렉터리 위치에 알맞게 수정해주세요.

     

    • 로거(logger) 설정
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    logger.addHandler(log_handler)

     - 로거 인스턴스를 생성하고, 시스템 로그 레벨을 'INFO' 로 설정합니다. 이는 INFO 이상의 로그만 기록하도록 합니다.

    (시스템 로그 레벨을 다뤄 로깅하는 방법도 있으나 이 글에서는 사용자가 임의의 레벨을 만들어 사용합니다)

     

    • log 함수 구현
    def log(level, *args):
        """Log messages with given level and messages."""
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        messages = "|".join(str(arg) for arg in args)
    
        levels = ('TR', 'DG', 'WA')
        if level not in levels:
            logger.info(f"TR|{now}|Log Level Error")
        else:
            logger.info(f"{level}|{now}|{messages}")

     - 로그 레벨과 메시지를 인자로 받아 생성된 로거 인스턴스를 통해 로그를 기록합니다.

     - 로그를 출력할 때 현재 시간('now')을 연결하여 메시지와 함께 출력합니다.

     - 로그 레벨은 위와 같이 3가지로 했으나 사용자가 원하는 이름, 원하는 개수로 수정할 수 있습니다.

     

     

    2) 로그 출력 확인

    • 로그가 정상 출력되는지 확인

    이제 실제로 로그가 잘 출력되는지 확인해 보겠습니다.

    테스트 함수('func()') 를 만들어서 생성한 log() 함수가 제대로 동작하는지 확인해 봅시다.

     

    코드

    더보기
    import os
    import logging
    from logging import handlers
    from datetime import datetime
    
    """ Logging Configuration """
    LOG_PATH = "D:\\Path\\to\\Logging"  # WINDOWS
    # LOG_PATH = "/Path/to/Logging"  # LINUX
    LOG_FILE = "upbit_bot"  # FILE NAME
    
    log_formatter = logging.Formatter('%(message)s')
    log_handler = handlers.TimedRotatingFileHandler(
        filename=os.path.join(LOG_PATH, LOG_FILE),
        when='midnight',
        interval=1,
        encoding='utf-8'
    )
    log_handler.setFormatter(log_formatter)
    log_handler.suffix = "%Y%m%d"
    
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    logger.addHandler(log_handler)
    
    def log(level, *args):
        """Log messages with given level and messages."""
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        messages = "|".join(str(arg) for arg in args)
    
        levels = ('TR', 'DG', 'WA')
        if level not in levels:
            logger.info(f"TR|{now}|Log Level Error")
        else:
            logger.info(f"{level}|{now}|{messages}")
    
    def func():
        try:
            log("DG", 'Func Message')
            res = 1
        except Exception as e:
            log("DG",e)
            res = 0
        return res
    
    if __name__ == "__main__":
        res = func()
        if res == 1:
            log('TR','Main Message')
        else:
            log('WA','Warning Message')

     

     

    3) 로그 확인 방법

    파일 I/O에 대한 기본적인 이해가 있으신 분들을 위해 접어놓겠습니다.

    더보기
    • [시작] -> [Windows PowerShell] 실행

     

    • 로그 파일 위치로 이동

     

    • 파일 조회 및 내용 모니터링

    'Get-Content "파일명" -Wait -Tail 10' 명령을 입력하면 해당 파일을 계속해서 모니터링 합니다.

    모니터링을 종료하려면 'Ctrl + C' 를 동시에 눌러줍시다.

     

     

     

    4) 데코레이터 설정

    저희는 파이썬을 공부하고자 하는게 아니죠?

    이제 로그를 어떻게 사용할 것인지를 살펴봅시다.

     

    • 데코레이터 설정 함수 추가
    def log_function_call(func):
        """Decorator to log function calls."""
        def wrapper(*args, **kwargs):
            params = ", ".join([str(arg) for arg in args])
            log("DG", "request", f"{func.__name__}({params})")
            return func(*args, **kwargs)
        return wrapper

     - 이는 호출한 함수명과 해당 함수에 입력된 parameter 정보를 로깅하는 기능을 합니다.

     - 함수가 호출될 때 'request' 라는 메시지를 출력합니다.

     

    • 사용법
    @log_function_call
    def func():
        try:
            log("DG", 'response', 'Func Message')
            res = 1
        except Exception as e:
            log("DG",e)
            res = 0
        return res

     - 정보를 출력할 함수명 바로위에 '@log_function_call' 를 적어주세요.

     - 함수 호출 결과를 나타내기 위해 'response' 라는 메시지를 출력합니다.

     

    • 출력 결과

     - 'request' 메시지와 'response' 메시지를 출력해 함수의 입/출력값을 확인할 수 있습니다.

     

    마무리 정리하자면..

    1) 실행 로그를 확인할 함수에 데코레이터 옵션 기재

    2) 'DG' 레벨은 함수들의 실행 흐름, 입력값, 출력값, 에러값 등을 출력

    3) 'TR' 레벨은 코드가 동작함에 따른 사용자가 확인해야 하는 값 출력

    4) 'WA' 레벨은 코드의 흐름상 문제가 있는 부분을 출력

    5) 로그를 출력해야하는 위치에 log('로그레벨', '메시지', ...) 입력

     

    오늘은 이렇게 필요한 위치에 로그를 출력하는 방법을 알아보았습니다. 

    파이썬에서 제공하는 기본 모듈을 활용해서 나만의 log() 함수를 만들어 보았습니다.

    자기만의 로그 설정하는 방법을 가지고 계신다면 이 글은 크게 의미는 없습니다.

    나름대로 간단하게 표현하고자 했는데 쉽게 사용하실지 모르겠네요.

     

    다음 게시물에서는 지금까지 작성한 코드를 기능별로 나눠 모듈화 하는 작업을 해보겠습니다.

    728x90
    반응형