웹사이트 검색

Python 3에서 로깅을 사용하는 방법


소개

logging 모듈은 표준 Python 라이브러리의 일부이며 소프트웨어가 실행되는 동안 발생하는 이벤트에 대한 추적을 제공합니다. 어떤 이벤트가 발생했는지 나타내기 위해 코드에 로깅 호출을 추가할 수 있습니다.

logging 모듈은 분석을 위해 사용자 트랜잭션의 이벤트를 기록하는 감사 로깅뿐만 아니라 응용 프로그램 작업과 관련된 이벤트를 기록하는 진단 로깅을 모두 허용합니다. 특히 이벤트를 파일에 기록하는 데 사용됩니다.

전제 조건

컴퓨터나 서버에 Python 3가 설치되어 있고 프로그래밍 환경이 설정되어 있어야 합니다. 프로그래밍 환경이 설정되지 않은 경우 운영 체제(Ubuntu, CentOS, Debian 등)에 적합한 서버의 프로그래밍 환경에 대한 설치 및 설정 가이드를 참조할 수 있습니다.

로깅 모듈을 사용하는 이유

logging 모듈은 프로그램 내에서 발생하는 이벤트를 기록하여 소프트웨어의 런타임 전체에서 발생하는 모든 이벤트와 관련된 출력을 볼 수 있도록 합니다.

코드 전체에서 print() 문을 사용하여 이벤트가 발생하는지 확인하는 것이 더 익숙할 수 있습니다. print() 문은 문제를 해결하기 위해 코드를 디버깅하는 기본적인 방법을 제공합니다. 코드 전체에 print() 문을 삽입하면 실행 흐름과 프로그램의 현재 상태를 추적할 수 있지만 이 솔루션은 몇 가지 이유:

  • 디버깅 출력과 정상 프로그램 출력이 혼재되어 있어 구분이 어렵습니다.
  • 코드 전체에 분산된 print() 문을 사용할 때 디버깅 출력을 제공하는 문을 비활성화하는 효율적인 방법이 없습니다.
  • 디버깅이 끝나면 모든 print() 문을 제거하기가 어려워집니다.
  • 쉽게 사용할 수 있는 진단 정보가 포함된 로그 레코드가 없습니다.

코드에서 logging 모듈을 사용하는 습관을 들이는 것이 좋습니다. 작은 Python 스크립트 이상으로 성장하는 애플리케이션에 더 적합하고 디버깅에 대한 지속 가능한 접근 방식을 제공하기 때문입니다.

로그는 시간 경과에 따른 동작과 오류를 보여줄 수 있기 때문에 애플리케이션 개발 프로세스에서 진행 중인 작업에 대한 전반적인 그림을 더 잘 보여줄 수도 있습니다.

콘솔에 디버그 메시지 출력

정보: 이 자습서의 예제 코드를 따라하려면 python3 명령을 실행하여 로컬 시스템에서 Python 대화형 셸을 엽니다. 그런 다음 >>> 프롬프트 뒤에 추가하여 예제를 복사, 붙여넣기 또는 편집할 수 있습니다.

프로그램에서 무슨 일이 일어나고 있는지 보기 위해 print() 문을 사용하는 데 익숙하다면 다음과 같은 것을 생성하는 클래스를 정의하고 객체를 인스턴스화하는 프로그램을 보는 데 익숙할 것입니다.

class Pizza():
    def __init__(self, name, price):
        self.name = name
        self.price = price
        print("Pizza created: {} (${})".format(self.name, self.price))

    def make(self, quantity=1):
        print("Made {} {} pizza(s)".format(quantity, self.name))

    def eat(self, quantity=1):
        print("Ate {} pizza(s)".format(quantity, self.name))

pizza_01 = Pizza("artichoke", 15)
pizza_01.make()
pizza_01.eat()

pizza_02 = Pizza("margherita", 12)
pizza_02.make(2)
pizza_02.eat()

위의 코드에는 Pizza 클래스 개체의 이름가격을 정의하는 __init__ 메서드가 있습니다. 그런 다음 피자를 만들기 위한 make()라는 메서드와 피자를 먹기 위한 eat()라는 두 가지 메서드가 있습니다. 이 두 메소드는 1에서 초기화되는 quantity의 매개변수를 받습니다.

이제 프로그램을 실행해 봅시다:

  1. python pizza.py

다음과 같은 결과를 받게 됩니다.

Output
Pizza created: artichoke ($15) Made 1 artichoke pizza(s) Ate 1 pizza(s) Pizza created: margherita ($12) Made 2 margherita pizza(s) Ate 1 pizza(s)

print() 문을 통해 코드가 작동하는지 확인할 수 있지만 대신 logging 모듈을 사용하여 이를 수행할 수 있습니다.

코드 전체에서 print() 문을 제거하거나 주석 처리하고 파일 맨 위에 import logging을 추가해 보겠습니다.

import logging


class Pizza():
    def __init__(self, name, value):
        self.name = name
        self.value = value
...

logging 모듈에는 import 문이 있습니다.

import logging

logging.basicConfig(level=logging.DEBUG)


class Pizza():
...

이 수준의 logging.DEBUG는 임계값을 설정하기 위해 위의 코드에서 참조하는 상수 정수 값을 나타냅니다. DEBUG의 수준은 10입니다.

이제 모든 print() 문을 logging.debug() 문으로 대체하겠습니다. 상수인 logging.DEBUG와 달리 logging.debug()logging 모듈의 메서드입니다. 이 메서드로 작업할 때 다음과 같이 print()에 전달된 동일한 문자열을 사용할 수 있습니다.

import logging

logging.basicConfig(level=logging.DEBUG)


class Pizza():
    def __init__(self, name, price):
        self.name = name
        self.price = price
        logging.debug("Pizza created: {} (${})".format(self.name, self.price))

    def make(self, quantity=1):
        logging.debug("Made {} {} pizza(s)".format(quantity, self.name))

    def eat(self, quantity=1):
        logging.debug("Ate {} pizza(s)".format(quantity, self.name))

pizza_01 = Pizza("artichoke", 15)
pizza_01.make()
pizza_01.eat()

pizza_02 = Pizza("margherita", 12)
pizza_02.make(2)
pizza_02.eat()

이 시점에서 python pizza.py 명령으로 프로그램을 실행하면 다음과 같은 출력이 표시됩니다.

Output
DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s)

로그 메시지에는 심각도 수준 DEBUGPython 모듈 수준을 나타내는 root라는 단어가 포함되어 있습니다. logging 모듈은 이름이 다른 로거 계층과 함께 사용할 수 있으므로 각 모듈에 대해 다른 로거를 사용할 수 있습니다.

예를 들어, 다른 이름과 다른 출력을 가진 다른 로거와 동일하게 로거를 설정할 수 있습니다.

logger1 = logging.getLogger("module_1")
logger2 = logging.getLogger("module_2")

logger1.debug("Module 1 debugger")
logger2.debug("Module 2 debugger")
Output
DEBUG:module_1:Module 1 debugger DEBUG:module_2:Module 2 debugger

logging 모듈을 사용하여 콘솔에 메시지를 출력하는 방법을 이해했으므로 logging 모듈을 사용하여 메시지를 파일로 출력하는 방법으로 넘어가겠습니다.

파일에 메시지 기록

logging 모듈의 주요 목적은 콘솔이 아닌 파일에 메시지를 기록하는 것입니다. 메시지 파일을 보관하면 시간이 지남에 따라 참조하고 정량화할 수 있는 데이터를 제공하여 코드에 어떤 변경이 필요한지 확인할 수 있습니다.

파일에 대한 로깅을 시작하려면 filename 매개변수를 포함하도록 logging.basicConfig() 메서드를 수정할 수 있습니다. 이 경우 파일 이름을 test.log라고 부르겠습니다.

import logging

logging.basicConfig(filename="test.log", level=logging.DEBUG)


class Pizza():
    def __init__(self, name, price):
        self.name = name
        self.price = price
        logging.debug("Pizza created: {} (${})".format(self.name, self.price))

    def make(self, quantity=1):
        logging.debug("Made {} {} pizza(s)".format(quantity, self.name))

    def eat(self, quantity=1):
        logging.debug("Ate {} pizza(s)".format(quantity, self.name))

pizza_01 = Pizza("artichoke", 15)
pizza_01.make()
pizza_01.eat()

pizza_02 = Pizza("margherita", 12)
pizza_02.make(2)
pizza_02.eat()

위의 코드는 인쇄할 로그의 파일 이름을 추가했다는 점을 제외하면 이전 섹션과 동일합니다. python pizza.py 명령으로 코드를 실행하면 test.log라는 디렉터리에 새 파일이 있어야 합니다.

nano(또는 선택한 텍스트 편집기)를 사용하여 test.log 파일을 열겠습니다.

  1. nano test.log

파일이 열리면 다음을 받게 됩니다.

DEBUG:root:Pizza created: artichoke ($15)
DEBUG:root:Made 1 artichoke pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: margherita ($12)
DEBUG:root:Made 2 margherita pizza(s)
DEBUG:root:Ate 1 pizza(s)

이것은 test.log 파일에 있다는 점을 제외하면 이전 섹션에서 본 콘솔 출력과 유사합니다.

CTRL + x로 파일을 닫고 코드를 수정할 수 있도록 pizza.py 파일로 다시 이동합니다.

대부분의 코드를 동일하게 유지하지만 pizza_01pizza_02의 두 피자 인스턴스에서 매개변수를 수정합니다.

import logging

logging.basicConfig(filename="test.log", level=logging.DEBUG)


class Pizza():
    def __init__(self, name, price):
        self.name = name
        self.price = price
        logging.debug("Pizza created: {} (${})".format(self.name, self.price))

    def make(self, quantity=1):
        logging.debug("Made {} {} pizza(s)".format(quantity, self.name))

    def eat(self, quantity=1):
        logging.debug("Ate {} pizza(s)".format(quantity, self.name))

# Modify the parameters of the pizza_01 object
pizza_01 = Pizza("Sicilian", 18)
pizza_01.make(5)
pizza_01.eat(4)

# Modify the parameters of the pizza_02 object
pizza_02 = Pizza("quattro formaggi", 16)
pizza_02.make(2)
pizza_02.eat(2)

이러한 변경으로 python pizza.py 명령으로 프로그램을 다시 실행해 보겠습니다.

프로그램이 실행되면 nano를 사용하여 test.log 파일을 다시 열 수 있습니다.

  1. nano test.log

파일을 검토하면 몇 개의 새로운 라인이 추가되고 프로그램이 마지막으로 실행된 이전 라인이 유지되었음을 알 수 있습니다.

DEBUG:root:Pizza created: artichoke ($15)
DEBUG:root:Made 1 artichoke pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: margherita ($12)
DEBUG:root:Made 2 margherita pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: Sicilian ($18)
DEBUG:root:Made 5 Sicilian pizza(s)
DEBUG:root:Ate 4 pizza(s)
DEBUG:root:Pizza created: quattro formaggi ($16)
DEBUG:root:Made 2 quattro formaggi pizza(s)
DEBUG:root:Ate 2 pizza(s)

이 정보는 확실히 유용하지만 추가 LogRecord 속성을 추가하여 로그를 더 많은 정보로 만들 수 있습니다. 주로 LogRecord가 생성된 시기를 알려주는 사람이 읽을 수 있는 타임 스탬프를 추가하고 싶습니다.

%(asctime)s 문자열이 있는 표에 표시된 대로 해당 속성을 format이라는 매개 변수에 추가할 수 있습니다. 또한 DEBUG 수준 이름을 유지하려면 문자열 %(levelname)s를 포함하고 로거가 출력하도록 요청하는 문자열 메시지를 유지해야 합니다. %(message)s를 포함합니다. 이러한 각 속성은 추가된 코드와 같이 콜론으로 구분됩니다.

import logging

logging.basicConfig(
    filename="test.log",
    level=logging.DEBUG,
    format="%(asctime)s:%(levelname)s:%(message)s"
    )


class Pizza():
    def __init__(self, name, price):
        self.name = name
        self.price = price
        logging.debug("Pizza created: {} (${})".format(self.name, self.price))

    def make(self, quantity=1):
        logging.debug("Made {} {} pizza(s)".format(quantity, self.name))

    def eat(self, quantity=1):
        logging.debug("Ate {} pizza(s)".format(quantity, self.name))

pizza_01 = Pizza("Sicilian", 18)
pizza_01.make(5)
pizza_01.eat(4)

pizza_02 = Pizza("quattro formaggi", 16)
pizza_02.make(2)
pizza_02.eat(2)

python pizza.py 명령으로 추가된 속성으로 위의 코드를 실행하면 test.log 파일에 human- DEBUG의 수준 이름과 로거에 문자열로 전달되는 관련 메시지 외에 읽을 수 있는 타임 스탬프.

DEBUG:root:Pizza created: Sicilian ($18)
DEBUG:root:Made 5 Sicilian pizza(s)
DEBUG:root:Ate 4 pizza(s)
DEBUG:root:Pizza created: quattro formaggi ($16)
DEBUG:root:Made 2 quattro formaggi pizza(s)
DEBUG:root:Ate 2 pizza(s)
2021-08-19 23:31:34,484:DEBUG:Pizza created: Sicilian ($18)
2021-08-19 23:31:34,484:DEBUG:Made 5 Sicilian pizza(s)
2021-08-19 23:31:34,484:DEBUG:Ate 4 pizza(s)
2021-08-19 23:31:34,484:DEBUG:Pizza created: quattro formaggi ($16)
2021-08-19 23:31:34,484:DEBUG:Made 2 quattro formaggi pizza(s)
2021-08-19 23:31:34,484:DEBUG:Ate 2 pizza(s)

필요에 따라 프로그램 파일의 로그를 관련성 있게 만들기 위해 코드에서 추가 LogRecord 특성을 사용할 수 있습니다.

디버그 및 기타 메시지를 별도의 파일에 기록하면 시간이 지남에 따라 Python 프로그램을 전체적으로 이해할 수 있으므로 프로그램에 입력된 기록 작업뿐만 아니라 발생하는 이벤트 및 거래.

로깅 수준 표

개발자는 심각도 수준을 추가하여 로거에 캡처된 이벤트에 중요도 수준을 부여할 수 있습니다. 심각도 수준은 아래 표에 나와 있습니다.

로깅 수준은 기술적으로 정수(상수)이며 숫자 값 0에서 로거를 초기화하는 NOTSET부터 시작하여 모두 10씩 증가합니다.

미리 정의된 수준과 관련하여 고유한 수준을 정의할 수도 있습니다. 동일한 숫자 값으로 수준을 정의하면 해당 값과 연결된 이름을 덮어씁니다.

다음 표에는 다양한 수준 이름, 해당 숫자 값, 수준을 호출하는 데 사용할 수 있는 기능 및 해당 수준의 용도가 나와 있습니다.

Level Numeric Value Function Used to
CRITICAL 50 logging.critical() Show a serious error, the program may be unable to continue running
ERROR 40 logging.error() Show a more serious problem
WARNING 30 logging.warning() Indicate something unexpected happened, or could happen
INFO 20 logging.info() Confirm that things are working as expected
DEBUG 10 logging.debug() Diagnose problems, show detailed information

logging 모듈은 WARNING에서 기본 수준을 설정하므로 WARNING, ERRORCRITICAL는 모두 기본적으로 기록됩니다. 위의 예에서 다음 코드를 사용하여 DEBUG 수준을 포함하도록 구성을 수정했습니다.

logging.basicConfig(level=logging.DEBUG)

공식 로깅 문서에서 명령 및 디버거 작업에 대한 자세한 내용을 읽을 수 있습니다.

결론

디버깅은 모든 소프트웨어 개발 프로젝트의 중요한 단계입니다. logging 모듈은 표준 Python 라이브러리의 일부이며, 소프트웨어가 실행되는 동안 발생하는 이벤트에 대한 추적을 제공하고 이러한 이벤트를 별도의 로그 파일에 출력하여 코드가 실행되는 동안 발생하는 상황을 추적할 수 있도록 합니다. 실행합니다. 이것은 시간이 지남에 따라 프로그램 실행에서 발생하는 다양한 이벤트를 이해하여 코드를 디버그할 수 있는 기회를 제공합니다.