• 2020. 8. 2.

    by. 윈썸지니

    반응형

    프로그램 개발시 동작 상태, 결과 또는 오류 발생시 내용들을 파일에 저장 해야 할 경우가 생깁니다.

     

    로그가 없다면 프로그램이 동작 했는데 정상 동작 했는지 ? 수행은 한 것인지? 를 알기 힘듭니다.

    또한 동작 중 오류가 발생했는데 어떤 오류가 발생해서 프로그램이 중지 되었는지를 알 수 있는 방법으로 외부 파일에

     

    메시지를 남기는 방법을 사용 합니다.

    이런 메시지들을 로그(log) 라고 합니다.

    프로그램 개발 중에도 디버깅을 위해 어떤 값을 출력해 보고자 할 경우도 사용 할 수 있습니다. 

     

    로그를 파일에 저장할 때 고려할 사항 들이 있습니다.

    1. 로그 형식

          - 일자, 시간, 메시지 등이 형식 정의

     

    2. 파일 관리

           - 로그 파일 개수 : 하나의 파일로 관리 또는 몇 개의 파일만 관리, 일자별로 파일 생성 등 여러가지 방법이 존재

           - 파일 크기 : 만약 하나의 파일에 관리하면 계속 쌓여서 파일 크기가 커집니다. 파일 크기에 따른 관리

           - 이전 로그 파일 삭제 : 만약 일자별 생성이면 예를 들어 3개월 정도의 로그 파일만 남기고 그 이전 파일은 삭제

     

    하나의 로그 파일에 동작시 마다 새로 쓸지?, 파일 끝에 추가 할지?, 파일 크기는 어떻게 관리 할지? 로그 파일 보관 주기는 어떻게 할지? 등을 결정하여 프로그램 해야 합니다. 로그 파일 관리 만으로도 하나의 프로그램이 됩니다. 이렇듯 고려할 사항과 관리해야 할 사항들이 많이 있습니다.

     

    파이썬은 위의 내용들을 해결 해 줄 수 있는 라이브러리를 제공합니다.

    logging 이라는 라이브러리를 사용해서 아주 쉽고 짧은 코딩으로 간편하게 해결 할 수 있습니다.

     

    아래 예제는 로그 파일에 프로그램 기동 시 로그 파일이 없으면 새로 만들어 주고 재 시작해도 같은 파일에 계속 추가합니다.

    특정 사이즈가 되면 백업 파일을 생성합니다. 이 백업 파일은 10개 까지 유지합니다.

    예를 들면 로그 파일 사이즈가 10MB10개 까지만 유지하는 예제 입니다.  10MB 파일이 최대 10개 까지만 생성이 됩니다.

    최대 10개가 되면 제일 처음에 쌓였던 로그 내용이 지우지면서 파일 10개로 로테이션 방식으로 최신 로그가 유지 됩니다.

     

    다음은 로그 남기는 예제 입니다. ( log_test.py )

    import logging

    import logging.handlers

    LOG_MAX_SIZE = 1024 *1024 * 10

    LOG_FILE_CNT = 10

    LOG_LEVEL = logging.INFO


    try:

        logger = logging.getLogger('log_test')

        logfile_H = logging.handlers.RotatingFileHandler( "./logs/log_test.log", maxBytes=LOG_MAX_SIZE, backupCount=LOG_FILE_CNT )

        formatter = logging.Formatter('[%(asctime)s|%(levelname)s|%(funcName)s|%(lineno)d] %(message)s')

        logfile_H.setFormatter(formatter)

        logger.addHandler(logfile_H)

        logger.setLevel( LOG_LEVEL )

       

        logger.info( "INFO 정보입니다")

        logger.debug( "DEBUG 정보입니다")

        logger.warning( "WARNING 정보입니다")

        logger.error( "ERROR 정보입니다")

        logger.critical( "CRITICAL 정보입니다")   


    except Exception as err:

        logger.error( err )

    위의 예제를 실행 해 봅니다.

     

    C:\py_test>python log_test.py                                

    C:\py_test>cd logs                                               

    C:\py_test\logs>dir                                             

     C:\py_test\logs 디렉터리                                     

    2020-08-02  오전 12:39    <DIR>          .                   

    2020-08-02  오전 12:39    <DIR>          ..                  

    2020-08-02  오전 12:39               256 log_test.log      

     

    logs 폴더 아래에 log_test.log 파일이 생성되었음을 확인합니다.

    log_test.py 파일의 내용을 확인해 보면 아래와 같습니다.

    C:\py_test\logs>type log_test.log

    [2020-08-02 00:39:31,997|INFO|<module>|16] INFO 정보입니다

    [2020-08-02 00:39:32,009|WARNING|<module>|18] WARNING 정보입니다

    [2020-08-02 00:39:32,010|ERROR|<module>|19] ERROR 정보입니다

    [2020-08-02 00:39:32,010|CRITICAL|<module>|20] CRITICAL 정보입니다

     

    로그 파일이 여러 개 생성하도록 소스를 좀 수정하여 테스트 해 봅니다.

    아래 내용만 수정 및 추가 하였습니다.

     

    LOG_MAX_SIZE = 1024 *1024 * 1       # 테스트를 위해 파일 최대 사이즈 수정 ( 1 MB )

     

    while True :                                                # 로그를 많이 쌓기 위해 무한 loop  문 추가

            logger.info( "INFO 정보입니다")

            logger.debug( "DEBUG 정보입니다")

     

    다시 실행해 봅니다. logs 폴더에 파일들이 생성 되는 것을 확인 후 무한 루프 프로그램이므로 Ctrl + C 로 멈춥니다.

    C:\py_test>python log_test.py

     

    아래 결과를 보면 1MB 10개의 백업 파일이 생성되는 것을 알 수 있습니다.

    그럼 가장 최신 로그를 확인하려면 맨 마지막 파일인 log_test.log.10 파일을 확인 하면 될까요?

    아닙니다. 최신 로그는 log_test.log 에 남습니다파일의 생성은 확장자가 1 부터 시작해서 10까지 만듭니다.

     

    log_test.log 파일에 로그를 쌓으면서 최대 사이즈가 되면 log_test.log.1 에 백업을 하고 log_test.log 파일에 쌓습니다.

    그럼 가장 오래된 로그를 확인하려면 가장 먼저 생성된 log_test.log.1 을 확인하면 될까요?

    아닙니다. log_test.log.10 을 확인하면 됩니다.

    최대 사이즈가 되면 1 -> 2가 되고 2->3 이 되고 이런 방식으로 로그 파일이 생성 됩니다.

     

    동작 확인을 했으니 이제 예제 소스에 관한 내용을 설명합니다.

     

    logging.getLogger('log_test’)

    그냥 위의 문장 없이 logger.info()를 사용해도 되지만 보통 고유한 로그를 따로 생성해서 사용합니다.

    위의 문장은 log_test 라는 로거를 생성하는 문장입니다.

     

    logging.handlers.RotatingFileHandler( "./logs/log_test.log", maxBytes=LOG_MAX_SIZE, backupCount=LOG_FILE_CNT )

    로그 파일명, 파일 최대 사이즈, 유지하는 파일 개수 등을 설정합니다.

    maxBytes = 1024 * 1024 * 10 10 MB 의 최대 사이즈를 표현합니다.

     

    logging.Formatter('[%(asctime)s|%(levelname)s|%(funcName)s|%(lineno)d] %(message)s’)

    formatter 는 로그 파일 내용 형식을 정의 합니다.

    결과 파일에 왼쪽에 아래와 같이 공통 형식이 있고 다름에 로그 메시지가 보여 집니다.

    이런 공통 형식을 정의 하고 로그 메시지만 출력해 주면 되므로 편리합니다.

    일자 시간, 로그레벨, 함수명, 소스라인, 로그메세지의 예제 입니다.  

    [2020-08-02 00:54:23,583|ERROR|<module>|20]

     

    setFormatter(formatter)

    위의 로그 형식을 정의 합니다.

    logger.addHandler(logfile_H)

    핸들러를 설정하는 내용인데 핸들러는 로그 정보가 출력되는 곳을 말합니다.

    화면, 소켓, 파일 등으로 출력 할 수 있습니다.

     

    print()  문 처럼 화면에 로그를 출력하고 싶으면 아래와 같이 하면 됩니다.

    stream_H = logging.StreamHandler()
    logger.addHandler(stream_hander)

    logger.setLevel( LOG_LEVEL )

    로그 레벨을 정의 하는 것으로 로그 레벨은 DEBUG < INFO < WARNING < ERROR < CRITICAL 순입니다.

    만약 로그 레벨이 WARNING 이면  DEBUG INFO 로그 메시지는 출력 되지 않습니다. 설정된 로그 레벨 이상으로 출력됩니다. WARNING, ERROR, CRITICAL 내용이 출력됩니다. 

    위의 예제에서 로그 레벨이 INFO 였으므로 DEBUG 내용은 출력되지 않았습니다.

     

    로그 레벨은 디폴트 설정은 WARNING  입니다.

    실제 로그 레벨 값은 DEBUG: 10, INFO :20, WARNING:30, ERROR:40, CRITICAL:50 입니다.

    logger.setLevel( logger.ERROR ) logger.setLevel(30) 은 같은 문장 입니다.

     

    프로그램 개발 중에는 로그 레벨을 DEBUG 로 해 놓고 운영시는 ERROR 정도로 해 놓으면 좋습니다.

    따라서 로그 레벨도 이전 포스팅에서 다뤘던 외부 파일로 설정 값 관리를 하면 좋습니다.

      

    logger.info( "INFO 정보입니다")

    logger.debug( "DEBUG 정보입니다")

    logger.warning( "WARNING 정보입니다")

    logger.error( "ERROR 정보입니다")

    logger.critical( "CRITICAL 정보입니다")   

     

    실제로 로그 메시지를 츨력 해 주는 함수로 로그 레벨에 따라 메시지가 출력되므로 적당한 함수를 사용하여 로그 메시지를 출력합니다.

     

    이상으로 로그 관리를 해 주는 logging 에 대해 알아 보았습니다.

    반응형