별과 인간이 그리는 무늬

데이터베이스 핸들러 사용하기(dbhandler, databasehandler, sqlitehandler) | 파이썬 loggining 모듈 본문

배움 | python 중급

데이터베이스 핸들러 사용하기(dbhandler, databasehandler, sqlitehandler) | 파이썬 loggining 모듈

moonui.bee 2021. 2. 4. 21:59

무늬비다.

글쓴이의 경우 파이썬을 스크립트를 만들때 기본 모듈인 logging 모듈을 꽤 유용하게 사용하는 편이다.

로깅이 필요한 거의 모든 상황에서 사용한다고 해도 무방할 정도일 듯 싶다.

 

logging 모듈을 사용할 때 아래에서 소개하는 기본제공되는 핸들러만 사용하여도 대부분 원하는 기능을 구현해낼 수 있다.

더보기

StreamHandler

- 인스턴스는 스트림(파일류 객체)에 메시지를 보냅니다.

FileHandler

- 인스턴스는 디스크 파일에 메시지를 보냅니다.

BaseRotatingHandler

- 특정 지점에서 로그 파일을 회전시키는 처리기의 베이스 클래스입니다. 직접 인스턴스화하는 것은 아닙니다. 
대신 RotatingFileHandler 또는 TimedRotatingFileHandler 를 사용하십시오.

RotatingFileHandler

- 인스턴스는 디스크 파일에 메시지를 보내는데, 최대 로그 파일 크기와 로그 파일 회전을 지원합니다.

TimedRotatingFileHandler

- 인스턴스는 디스크 파일에 메시지를 보내는데, 일정한 시간 간격으로 로그 파일을 회전시킵니다.

SocketHandler

- 인스턴스는 TCP/IP 소켓에 메시지를 보냅니다. 3.4부터, 유닉스 도메인 소켓도 지원됩니다.

DatagramHandler

- 인스턴스는 UDP 소켓에 메시지를 보냅니다. 3.4부터, 유닉스 도메인 소켓도 지원됩니다.

SMTPHandler

- 인스턴스는 지정된 전자 우편 주소로 메시지를 보냅니다.

SysLogHandler

- 인스턴스는 유닉스 syslog 데몬(원격 기계에 있는 것도 가능합니다)에 메시지를 보냅니다.

NTEventLogHandler

- 인스턴스는 윈도우 NT/2000/XP 이벤트 로그에 메시지를 보냅니다.

MemoryHandler

- 인스턴스는 메모리에 있는 버퍼에 메시지를 보내는데, 특정 기준이 만족 될 때마다 플러시 됩니다.

HTTPHandler

-인스턴스는 GET 또는 POST 을 사용해서 HTTP 서버에 메시지를 보냅니다.

WatchedFileHandler

- 인스턴스는 그들이 로깅하고 있는 파일을 감시합니다. 파일이 변경되면 닫히고 파일 이름을 사용하여 다시 열립니다. 이 처리기는 유닉스 계열 시스템에서만 유용합니다. 윈도우는 사용된 하부 메커니즘을 지원하지 않습니다.

QueueHandler

- 인스턴스는 queue 또는 multiprocessing 모듈에 구현된 것과 같은 큐로 메시지를 보냅니다.

NullHandler

- 인스턴스는 에러 메시지로 아무것도 하지 않습니다. 라이브러리 개발자가 로깅을 사용하지만, 라이브러리 사용자가 로깅을 구성하지 않으면 표시될 수 있는 〈No handlers could be found for logger XXX〉 라는 메시지를 피하려고 할 때 사용합니다. 자세한 정보는 라이브러리 로깅 구성 를 보십시오.

출처: https://docs.python.org/ko/3/howto/logging.html#logging-advanced-tutorial

 

다만 한가지 아쉬운 점은 데이터베이스에 로그를 저장하는 핸들러는 제공하지 않는다.

그래서 DB에 로그를 저장할 수 있는 DatabaseHandler를 사용하는 방법을 소개해 보려고 한다.

 

database에는 여러종류가 있지만 설명에 용이하도록 기본으로 제공하는 sqlite3 db를 이용하는  DatabaseHanlder를 제작하도록 하겠다.

 

step1. 먼저 logging.Handler의 속성을 그대로 상속받는 DatabaseHandler 클랙스를 생성한다.

class DatabaseHandler(logging.Handler):
    def __init__(self):
        pass

step2. 클래스 초기화 함수(__init__)에 아래와 같은 내용을 정의한다

        - 상속받은 logging.hanlder 초기화

        - sqlite3 db connect 및 cursor 설정(moonuibee.db 파일 생성)

        - db table 생성(log)

class DatabaseHandler(logging.Handler):
    def __init__(self):
        logging.Handler.__init__(self)
        self.database = 'moonuibee.db'
        self.conn = sqlite3.connect(self.database)
        self.cur = self.conn.cursor()

        mk_table_query = '''
            CREATE TABLE IF NOT EXISTS log (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                insertTime TEXT,
                logLv TEXT,
                filename TEXT,
                lineno INTEGER,
                message TEXT
                )
        '''
        self.conn.execute(mk_table_query)
        self.conn.commit()

step3. inserTime col에 입력할 시간값을 리턴하는 함수를 정의한다.

        logging 모듈에서 시간값은 timestamp형태로 리턴되어

        formatter를 이용하여 출력 시 원하는 모양으로 출력되도록 변경할 수 있다.

        db에 입력할때는 따로 formatter를 사용하지 않으므로 따로 설정해주도록 한다.

        통일성을 해치지 않으려는 습관때문에 record를 입력값으로 사용했으나,

        시간값만 리턴할 수 있으면 어느방법으로 메소드를 제작하여도 문제되지 않는다.

    def time_format(self, record):
        self.now = datetime.datetime.now()
        record.nowstr = self.now.strftime('%Y-%m-%d %H:%M:%S')

step4. 이벤트 함수(emit)을 정의한다.

         - 트리거가 record 값이므로 반드시 입력값으로 사용

         - formatter의 format을 그대로 사용하기 위해 self.format(record) 삽입

         - dbtable에 입력

    def emit(self, record):
        self.format(record)
        self.time_format(record)
        insert_db_query = '''
            INSERT INTO log (
            insertTime, logLv, filename, lineno, message) VALUES
            ('{}', '{}', '{}', {}, '{}')
        '''.format(record.nowstr, record.levelname, record.filename, record.lineno, record.message)

        self.conn.execute(insert_db_query)
        self.conn.commit()

step5. 데이터베이스 connection close

    def __del__(self):
        try:
            self.conn.close()
        except:
            pass

 step1 부터 step5를 모두 합치면 아래와 같다.

import logging, sqlite3, datetime

class DatabaseHandler(logging.Handler):
    def __init__(self):
        logging.Handler.__init__(self)
        self.database = 'moonuibee.db'
        self.conn = sqlite3.connect(self.database)
        self.cur = self.conn.cursor()

        mk_table_query = '''
            CREATE TABLE IF NOT EXISTS log (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                insertTime TEXT,
                logLv TEXT,
                filename TEXT,
                lineno INTEGER,
                message TEXT
                )
        '''
        self.conn.execute(mk_table_query)
        self.conn.commit()

    def time_format(self, record):
        self.now = datetime.datetime.now()
        record.nowstr = self.now.strftime('%Y-%m-%d %H:%M:%S')

    def emit(self, record):
        self.format(record)
        self.time_format(record)
        insert_db_query = '''
            INSERT INTO log (
            insertTime, logLv, filename, lineno, message) VALUES
            ('{}', '{}', '{}', {}, '{}')
        '''.format(record.nowstr, record.levelname, record.filename, record.lineno, record.message)

        self.conn.execute(insert_db_query)
        self.conn.commit()

    def __del__(self):
        try:
            self.conn.close()
        except:
            pass

 

사용하는 방법은 로깅모듈에서 핸들러를 추가하는 방법과 정확히 동일하다.

if __name__ == '__main__':
    logger = logging.getLogger('moonuibee')     # logger name 선언
    logger.setLevel(logging.DEBUG)              # logger level 설정
    sqliteHandler = DatabaseHandler()           # database handler 선언
    logger.addHandler(sqliteHandler)            # database hanlder 추가
    logger.debug("debug message")               # debug 메세지 작성
    logger.info("info message")                 # info 메세지 작성
    logger.warning("warning message")           # warning 메세지 작성
    logger.critical("critical message")         # critical 메세지 작성

 

수행결과를 moonuibee.db 파일을 sqlite viewer로 열어서 확인해보자

의도한대로 database 테이블에 loggng 메세지가 입력된 것을 확인할 수 있다.

서버에 설치해서 사용하는 db에도 connection 부분만 손보면 그대로 사용할 수 있다.

Comments