소개
이 섹션에서는 로깅 모듈에 대해 간략하게 소개합니다.
이 섹션에서는 로깅 모듈에 대해 간략하게 소개합니다.
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, ## 출력 레벨
)
일반적으로, 이것은 프로그램 시작 시 한 번 수행되는 설정입니다. 설정은 로깅 호출을 하는 코드와 분리되어 있습니다.
로깅은 매우 구성 가능합니다. 출력 파일, 레벨, 메시지 형식 등 모든 측면을 조정할 수 있습니다. 하지만 로깅을 사용하는 코드는 이에 대해 걱정할 필요가 없습니다.
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')
>>>
애플리케이션에 로깅을 추가하려면, 메인 모듈에서 로깅 모듈을 초기화하는 메커니즘이 필요합니다. 이를 수행하는 한 가지 방법은 다음과 같은 설정 코드를 포함하는 것입니다.
## 이 파일은 로깅 모듈의 기본 구성을 설정합니다.
## 필요에 따라 로깅 출력을 조정하려면 여기에서 설정을 변경하십시오.
import logging
logging.basicConfig(
filename = 'app.log', ## 로그 파일의 이름 (stderr를 사용하려면 생략)
filemode = 'w', ## 파일 모드 ('a'를 사용하여 추가)
level = logging.WARNING, ## 로깅 레벨 (DEBUG, INFO, WARNING, ERROR, 또는 CRITICAL)
)
다시 말하지만, 이 코드를 프로그램의 시작 단계 어딘가에 넣어야 합니다. 예를 들어, report.py 프로그램의 어디에 이 코드를 넣을 수 있습니까?
축하합니다! 로깅 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 기술을 향상시킬 수 있습니다.