로깅 모듈 소개

Beginner

This tutorial is from open-source community. Access the source code

소개

이 섹션에서는 로깅 모듈에 대해 간략하게 소개합니다.

로깅 모듈

logging 모듈은 진단 정보를 기록하기 위한 표준 라이브러리 모듈입니다. 또한 매우 정교한 기능을 갖춘 매우 큰 모듈이기도 합니다. 이 모듈의 유용성을 설명하기 위해 간단한 예제를 보여드리겠습니다.

예외 다시 살펴보기

연습 문제에서 다음과 같은 parse() 함수를 작성했습니다.

## fileparse.py
def parse(f, types=None, names=None, delimiter=None):
    records = []
    for line in f:
        line = line.strip()
        if not line: continue
        try:
            records.append(split(line,types,names,delimiter))
        except ValueError as e:
            print("Couldn't parse :", line)
            print("Reason :", e)
    return records

try-except 문에 집중해 봅시다. except 블록에서 무엇을 해야 할까요?

경고 메시지를 출력해야 할까요?

try:
    records.append(split(line,types,names,delimiter))
except ValueError as e:
    print("Couldn't parse :", line)
    print("Reason :", e)

아니면 조용히 무시해야 할까요?

try:
    records.append(split(line,types,names,delimiter))
except ValueError as e:
    pass

두 가지 해결책 모두 만족스럽지 않습니다. 왜냐하면 종종 두 가지 동작 (사용자가 선택 가능) 을 모두 원하기 때문입니다.

로깅 사용하기

logging 모듈은 이 문제를 해결할 수 있습니다.

## fileparse.py
import logging
log = logging.getLogger(__name__)

def parse(f,types=None,names=None,delimiter=None):
    ...
    try:
        records.append(split(line,types,names,delimiter))
    except ValueError as e:
        log.warning("Couldn't parse : %s", line)
        log.debug("Reason : %s", e)

코드는 경고 메시지 또는 특수한 Logger 객체를 발행하도록 수정되었습니다. logging.getLogger(__name__)으로 생성된 객체입니다.

로깅 기본 사항

로거 (logger) 객체를 생성합니다.

log = logging.getLogger(name)   ## name 은 문자열입니다.

로그 메시지 발행.

log.critical(message [, args])
log.error(message [, args])
log.warning(message [, args])
log.info(message [, args])
log.debug(message [, args])

각 메서드는 서로 다른 심각도 레벨을 나타냅니다.

이들은 모두 형식화된 로그 메시지를 생성합니다. args% 연산자와 함께 메시지를 생성하는 데 사용됩니다.

logmsg = message % args ## 로그에 기록됨

로깅 설정

로깅 동작은 별도로 구성됩니다.

## main.py

...

if __name__ == '__main__':
    import logging
    logging.basicConfig(
        filename  = 'app.log',      ## 로그 출력 파일
        level     = logging.INFO,   ## 출력 레벨
    )

일반적으로, 이것은 프로그램 시작 시 한 번 수행되는 설정입니다. 설정은 로깅 호출을 하는 코드와 분리되어 있습니다.

주석

로깅은 매우 구성 가능합니다. 출력 파일, 레벨, 메시지 형식 등 모든 측면을 조정할 수 있습니다. 하지만 로깅을 사용하는 코드는 이에 대해 걱정할 필요가 없습니다.

연습 문제 8.2: 모듈에 로깅 추가하기

fileparse.py에는 잘못된 입력으로 인한 예외 처리에 관련된 몇 가지 오류 처리가 있습니다. 다음과 같습니다.

## fileparse.py
import csv

def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
    '''
    CSV 파일을 타입 변환을 포함한 레코드 목록으로 파싱합니다.
    '''
    if select and not has_headers:
        raise RuntimeError('select requires column headers')

    rows = csv.reader(lines, delimiter=delimiter)

    ## Read the file headers (if any)
    headers = next(rows) if has_headers else []

    ## If specific columns have been selected, make indices for filtering and set output columns
    if select:
        indices = [ headers.index(colname) for colname in select ]
        headers = select

    records = []
    for rowno, row in enumerate(rows, 1):
        if not row:     ## Skip rows with no data
            continue

        ## If specific column indices are selected, pick them out
        if select:
            row = [ row[index] for index in indices]

        ## Apply type conversion to the row
        if types:
            try:
                row = [func(val) for func, val in zip(types, row)]
            except ValueError as e:
                if not silence_errors:
                    print(f"Row {rowno}: Couldn't convert {row}")
                    print(f"Row {rowno}: Reason {e}")
                continue

        ## Make a dictionary or a tuple
        if headers:
            record = dict(zip(headers, row))
        else:
            record = tuple(row)
        records.append(record)

    return records

진단 메시지를 출력하는 print 문을 확인하세요. 이러한 print 문을 로깅 작업으로 바꾸는 것은 비교적 간단합니다. 다음과 같이 코드를 변경합니다.

## fileparse.py
import csv
import logging
log = logging.getLogger(__name__)

def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
    '''
    CSV 파일을 타입 변환을 포함한 레코드 목록으로 파싱합니다.
    '''
    if select and not has_headers:
        raise RuntimeError('select requires column headers')

    rows = csv.reader(lines, delimiter=delimiter)

    ## Read the file headers (if any)
    headers = next(rows) if has_headers else []

    ## If specific columns have been selected, make indices for filtering and set output columns
    if select:
        indices = [ headers.index(colname) for colname in select ]
        headers = select

    records = []
    for rowno, row in enumerate(rows, 1):
        if not row:     ## Skip rows with no data
            continue

        ## If specific column indices are selected, pick them out
        if select:
            row = [ row[index] for index in indices]

        ## Apply type conversion to the row
        if types:
            try:
                row = [func(val) for func, val in zip(types, row)]
            except ValueError as e:
                if not silence_errors:
                    log.warning("Row %d: Couldn't convert %s", rowno, row)
                    log.debug("Row %d: Reason %s", rowno, e)
                continue

        ## Make a dictionary or a tuple
        if headers:
            record = dict(zip(headers, row))
        else:
            record = tuple(row)
        records.append(record)

    return records

이제 이러한 변경을 했으므로, 잘못된 데이터에 코드를 사용해 보세요.

>>> import report
>>> a = report.read_portfolio('missing.csv')
Row 4: Bad row: ['MSFT', '', '51.23']
Row 7: Bad row: ['IBM', '', '70.44']
>>>

아무것도 하지 않으면 WARNING 레벨 이상에 대한 로깅 메시지만 받게 됩니다. 출력은 간단한 print 문처럼 보일 것입니다. 그러나 로깅 모듈을 구성하면 로깅 레벨, 모듈 등에 대한 추가 정보를 얻을 수 있습니다. 다음 단계를 입력하여 확인하십시오.

>>> import logging
>>> logging.basicConfig()
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23']
WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44']
>>>

log.debug() 작업의 출력이 표시되지 않는 것을 알 수 있습니다. 레벨을 변경하려면 다음을 입력하십시오.

>>> logging.getLogger('fileparse').setLevel(logging.DEBUG)
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23']
DEBUG:fileparse:Row 4: Reason: invalid literal for int() with base 10: ''
WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44']
DEBUG:fileparse:Row 7: Reason: invalid literal for int() with base 10: ''
>>>

가장 중요한 로깅 메시지를 제외한 모든 메시지를 끄십시오.

>>> logging.getLogger('fileparse').setLevel(logging.CRITICAL)
>>> a = report.read_portfolio('missing.csv')
>>>

연습 문제 8.3: 프로그램에 로깅 추가하기

애플리케이션에 로깅을 추가하려면, 메인 모듈에서 로깅 모듈을 초기화하는 메커니즘이 필요합니다. 이를 수행하는 한 가지 방법은 다음과 같은 설정 코드를 포함하는 것입니다.

## 이 파일은 로깅 모듈의 기본 구성을 설정합니다.
## 필요에 따라 로깅 출력을 조정하려면 여기에서 설정을 변경하십시오.
import logging
logging.basicConfig(
    filename = 'app.log',            ## 로그 파일의 이름 (stderr를 사용하려면 생략)
    filemode = 'w',                  ## 파일 모드 ('a'를 사용하여 추가)
    level    = logging.WARNING,      ## 로깅 레벨 (DEBUG, INFO, WARNING, ERROR, 또는 CRITICAL)
)

다시 말하지만, 이 코드를 프로그램의 시작 단계 어딘가에 넣어야 합니다. 예를 들어, report.py 프로그램의 어디에 이 코드를 넣을 수 있습니까?

요약

축하합니다! 로깅 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 기술을 향상시킬 수 있습니다.