dinist

logging, signal, argparse 모듈 사용해보기 본문

개발?/파이썬

logging, signal, argparse 모듈 사용해보기

dinist 2020. 12. 13. 00:12

파이썬으로 리눅스에 데몬을 띄우는것에 대해 찾아보고 있었는데 좋은 정보가 있는 블로그를 찾았다.

우선 코드부터 이해하고 연습하고나서 데몬을 띄우든 뭘 하든 해야겠다.

 

참고 링크 1 : medium.com/@HatusneMiku3939/%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%8D%B0%EB%AA%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-python-1-3c5ea10c6366

 

리눅스 데몬 만들기(python) #1

최근에 데몬을 하나 만들일이 생겨서 기억을 더듬어 가면서 코딩을 했다. 너무 오래전에 해본거라 기억이 가물가물해서 시간을 조금 잡아 먹었다. 잊어먹지 않도록 내용을 정리해봤다.

medium.com

 

블로그에서 예시로 제시한 코드에서 내가 사용해보지 않은 모듈들이 있었다.

(파이썬은 기초수준만 다루어봤다)

 

실습한 경험을 기록하고 활용하여 내가 만든 연습용 프로그램을 기록해보기로 한다.

 

import time,logging,os,argparse

class Teller:
    str = None
    time_set = None

    def __init__(self, say = None):
        logging.basicConfig(level=logging.INFO)
        self.logger = logging.getLogger("Teller")

        if say:
            self.str = say.Say
            self.time_set = say.Sleep

    def play(self):
        self.logger.info("프로세스를 시작했습니다. PID는 {0}입니다.".format(os.getpid()))
        count = 0
        while True:
            self.logger.info("{0}(이)라고 {1}번째 외칩니다.".format(self.str, count))
            count += 1
            time.sleep(int(self.time_set))

if __name__ == '__main__':
    a_parser = argparse.ArgumentParser()
    a_parser.add_argument("-Say", help="외치고싶은 말을 입력하세요", default="No Comment")
    a_parser.add_argument("-Sleep", help="다음 출력의 시간 간격을 설정합니다.", default=1)
    ment = a_parser.parse_args()
    teller = Teller(ment)
    teller.play()

 

-Say 매개변수에 입력되는값이 외치고싶은 말의 내용이 되고,

-Sleep 매개변수에 입력되는값이 logger.info의 내용이 출력되는 시간의 간격을 지정하도록 설정하였다.

 

logging.basicConfig의 경우에는 logging모듈을 사용할때 기본적인 설정을 하는것이다.

level 포맷은 로거의 수준을 지정한 수준으로 설정한다. 즉, basicConfig에서 지정한 수준 이상의 로그만 출력한다는 의미이다.

 

logging.getLogger("이름") -> 이름을 지정할 경우 지정한 이름의 Logger를 반환한다. 여기서 이름은 Logger의 별칭이지 Logger를 담고있는 변수의 이름이 아니다. 이름을 지정하지 않을경우 RootLogger가 반환된다. 

 

Teller 클래스의 생성자 매개변수중에 say가 있는데 이 say는 main에서 입력되는 명령들을 받아 각 변수에 저장하기 위함이다.

 

그리고 play 함수 내에서 os.getpid 함수를 사용하는데 이는 해당 프로세스의 pid를 반환하는 함수이다.

 

프로그램의 main을 보면 argparse를 통해 ArgumentParser 인스턴스를 생성하고

받아들일 수 있는 매개변수를 설정할 수 있다. add_argument 함수의 첫번째 매개변수는 명령어를 지정하는 것이고, 두번째로 입력한 help는 help 명령을 입력했을때 표시할 도움말 내용을 지정한다.

그리고 세번째로 default는 해당 매개변수를 입력하지 않았을때 기본적으로 설정할 값을 지정하는 것이다.

 

이렇게 입력받은 값을 parse_args()명령으로 파싱 후 Teller클래스의 인스턴스를 생성하며 전달인자로 전달하고

Teller클래스의 play함수를 실행한다.

 

일단 여기까지 작성한 코드를 실행해보자

-h 옵션을 추가하여 도움말을 출력하게 하였다. 도움말이 잘 출력된다.

이제 매개변수 값도 추가하여 명령을 실행해보자.

 

"모두 화이팅" 이라는 말을 5초에 한번씩 출력하도록 하였다. (캡쳐된 사진이라 5초에 한번씩 되는모습은 보여주지 못함)

 

이번에는 Ctrl + c를 입력할때 전달되는 신호 SIGINT와 kill명령으로 종료시 전달되는 SIGTERM을 핸들링 해보자

(그냥 kill로 종료하면 기본 신호인 SIGTERM으로 종료되고 kill -9로 종료하면 SIGKILL이 된다.)

 

 

소스코드는 다음과 같다.

 

import time, logging, os, argparse, signal, sys


class Teller:
    Signal = {
        2: "인터럽트",
        15: "kill"
    }
    str = None
    time_set = None
    worker = True

    def __init__(self, say):
        logging.basicConfig(level=logging.INFO)
        self.logger = logging.getLogger()

        if say:
            self.str = say.Say
            self.time_set = say.Sleep

        signal.signal(signal.SIGINT, self.sig_handler)
        signal.signal(signal.SIGTERM, self.sig_handler)

    def play(self):
        self.logger.info("프로세스를 시작했습니다. PID는 {0}입니다.".format(os.getpid()))
        count = 0
        while self.worker:
            self.logger.info("{0}(이)라고 {1}번째 외칩니다.".format(self.str, count))
            count += 1
            time.sleep(int(self.time_set))

    def sig_handler(self, signum, occur_point):
        self.logger.info("{0}명령 발생으로 인하여 프로그램이 종료되었습니다.".format(self.Signal[signum]))
        sys.exit(9)
        self.worker = False


if __name__ == '__main__':
    a_parser = argparse.ArgumentParser()
    a_parser.add_argument("-Say", help="외치고싶은 말을 입력하세요", default="No Comment")
    a_parser.add_argument("-Sleep", help="다음 출력의 시간 간격을 설정합니다.", default=1)
    ment = a_parser.parse_args()
    teller = Teller(ment)
    teller.play()

 

인터럽트 감지시 이에대한 동작을 전달할 signal.signal 함수와 인터럽트 발생을 감지할 경우 진행할

함수(sig_handler)를 추가하였다.

 

signal.signal 함수를 통해 sig_handler 함수에 전달되는 전달인자는 두개인데, 첫번째는 signum (시그널의 숫자), 두번째는 프로그램의 몇번째 라인실행중 시그널이 발생했는지를 알려준다고 생각하면 된다.

 

실행을 해보자

이번에는 리눅스환경의 ubuntu에서 실행해본다.

ctrl + c 입력으로 인터럽트 신호를 줬더니 인터럽트 발생으로인하여 종료된다고 출력된다.

 

이번에는 kill명령으로 프로그램을 종료해보자.

 

pid를 찾기위해 ps명령으로 pid를 찾는다. 이후 kill명령으로 정상종료(SIGTERM) 시킨다.

 

만약 강제종료에 대한 핸들링도 추가하고싶다면 Signal 딕셔너리에 해당하는 값을 추가한다. ( 9: "강제종료" )

그리고 kill명령시에는 kill -9 pid 명령으로 강제종료하면 된다.

 

 

이렇게 kill명령발생으로 인한 프로그램종료라고 출력된다.

 

 

여기까지 logging,argparse,signal 모듈을 사용한 간단한 프로그램 연습을 마친다.