DeepLearning 관련 github python 코드를 참고하다가
<logger = logging.getLogger(__name__)>이라는 코드를 보고
logging에 대해 검색해보며 정리한 글입니다.
python 기초 로깅 자습서 | (자료 출처)
로깅(logging)이란?
소프트웨어가 실행될 때, 발생하는 event를 추적하는 수단
개발자 입장에서는, 코드에 logging을 추가해서, 특정 event가 발생했음을 나타낼 수 있음
개인적인 해석으로, logging은 시각적으로 보이는 출력과 보이지 않는 출력 모두를, 저장하고 옮기는 수단이 될 수 있음
logging의 함수?
debug(), info(), warning(), error(), critical() 이 있음
▷logging.info() or logging.debug()
- 프로그램의 정상 작동 중에서 발생하는 event를 보고함 (상태 모니터링 or 결함 등 조사)
- logging.debug()는 상태 진단 목적 등으로 아주 자세한 출력이 필요로 할 때 사용함.
▷warning.warn()
- 문제를 생기지 않게 하거나 경고를 제거하기 위해서, 클라이언트 응용 프로그램이 수정되어야 하는 경우, 특정 실행시간 event와 관련하여 경고를 발행.
▷logging.warning()
- 클라이언트 응용 프로그램이 할 수 있는 일이 없는 상황이지만 event를 계속 주목해야 하는 경우, 특정 실행시간 event와 관련하여 경고를 발행.
▷logging.error(), logging.exception(), logging.critical()
- 구체적인 에러 & 응용 프로그램 영역에 사용, 예외를 발생시키지 않고 에러의 억제를 보고함.
▷print()
- 명령행 스크립트 또는 일반적인 출력을 보기 위해, 콘솔에서 출력을 확인.
logging 함수의 수준?
debug(), info(), warning(), error(), critical()와 같은 "logging의 함수"는 추적되는 event의 수준(Level) 또는 심각도(Levelname)에 따라 명명된 것임
수준 | 특징 |
DEBUG | 상세한 정보를 보여줌. 보통 문제를 진단할 때만 필요함. |
INFO | 예상대로 작동하는지에 대한 확인. |
WARNING | 예상치 못한 일이 발생했거나 가까운 미래에 발생할 문제에 대한 표시를 해줌. |
ERROR | 더욱 심각한 문제로 인해 소프트웨어가 일부 기능을 수행하지 못함. |
CRITICAL | 심각한 에러. 프로그램 자체가 계속 실행되지 않을 수 있음을 나타냄. |
기본 로깅의 수준은 "WARNING"
logging 패키지가 수정되지 않는 한, WARNING 수준 이상의 event만 추적됨
(가령, logging.basicConfig(level=logging.INFO) 와 같은 방법으로 logging 패키지를 수정해서 logging의 기본 수준을 INFO 수준으로 설정할 수 있음. 원래는 WARNING 수준이 default 값)
수준: DEBUG < INFO < WARNING < ERROR < CRITICAL (왼쪽으로 갈수록 깊음)
ex) WARNING 수준 이상의 event만 추적되는 것
import logging
logging.warning('Watch out!') # will print a message to the console
logging.info('I told you so') # will not print anything
Console-------------------------------------------------------------
WARNING:root:Watch out!
logging 함수의 기본 수준이 WARNING 이므로, INFO 메세지는 나타나지 않음
콘솔에는 "logging 함수의 수준"과 "logging 호출에 제공된 event의 설명(='Watch out!')"이 나타남
root는 root logger를 나타냄. 뒤에서 설명
ex) 파일에 logging 하기
import logging
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
logging.error('And non-ASCII stuff, too, like Øresund and Malmö')
Console-------------------------------------------------------------
DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
ERROR:root:And non-ASCII stuff, too, like Øresund and Malmö
logging.basicConfig에서 "level"인자에 logging.DEBUG를 줌으로써, logging의 수준 임계값을 DEBUG로 설정했기 때문에, WARNING과 ERROR의 출력이 보여질 뿐만 아니라, DEBUG와 INFO의 수준도 보여짐.
(DEBUG < INFO < WARNING < ERROR < CRITICAL)
logging.basicConfig() 좀 더 알아보기
기본 Formatter로 StreamHandler를 생성하고, root logger에 추가하여 logging 시스템의 기본 구성을 수행해줌.
함수 debug(), info(), warning(), error(), critical()은 root logger에 처리기가 정의되어 있지 않으면 자동으로 basicConfig()를 호출
Formatter란?
객체로써 LogRecord를 사람이나 외부 시스템이 해석 할 수 있는 문자열로 변환하는 역할을 함.
StreamHandler란?
logging 패키지에 있는 핵심 클래스로써, sys.stdout 또는 sys.stderr, 파일류 객체와 같은 Stream으로 logging의 출력을 보냄. (write()와 flush() 메서드를 지원하는 모든 객체에게 logging의 출력을 보냄)
LogRecord, Handler 등의 객체가 궁금하면 아래 사진과 글의 후반부 내용 참고
ex) 여러 모듈(여러 .py 파일)에서 logging 활용
# myapp.py----------------------------------------------------------------
import logging
import mylib
def main():
logging.basicConfig(filename='myapp.log', level=logging.INFO)
logging.info('Started')
mylib.do_something()
logging.info('Finished')
if __name__ == '__main__':
main()
# mylib.py-----------------------------------------------------------------
import logging
def do_something():
logging.info('Doing something')
Console(myapp.py를 실행)-----------------------------------------------------
INFO:root:Started
INFO:root:Doing something
INFO:root:Finished
# py 파일 이름이 myapp이면, basicConfig 설정 해줄 때, filename인자로 myapp.log로 해야함
# 위 main() 함수의 첫째줄 코드 참고
logging 포멧?
%(levelname)s, %(message)s, %(asctime)s 등이 있음
logging.basicConfig() 함수에 format 인자로 위와 같은 문자열을 입력하면 됨.
ex) logging format
import logging
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
logging.debug('This message should appear on the console')
logging.info('Hellow')
logging.warning('World')
Console-------------------------------------------------------------
INFO: Hellow
WARNING: World
%(levelname)s, %(message)s 포멧 (심각도, event 설명)
import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')
Console-------------------------------------------------------------
2021-03-25 15:557:42,612 is when this event was logged.
%(asctime)s 포멧 (날짜/시간)
logging 라이브러리는 모듈 방식으로 구성되며
Logger, LogRecord, Handler, Filter, Formatter와 같은 여러 범주의 객체가 있음
Logger | 응용 프로그램 코드가 직접 사용하는 인터페이스를 노출 |
Handler | Logger에 의해 만들어진 LogRecord를 적절한 목적지로 보냄 |
Filter | 출력할 LogRecord를 결정하기 위한 보다 정밀한 기능을 제공 |
Formatter | 최종 출력에서 로그 레코드의 배치를 지정 |
* log event의 정보는 LogRecord 인스턴스를 통해 Logger, Handler, Filter, Formatter에 전달됨
Logger?
지금까지 공부한 logging은 결국 Logger 클래스의 인스턴스의 메서드를 호출하여 수행하게 되는 것
예를 들어, do_somthing.py을 실행하면(=Logger의 인스턴스의 메서드를 호출하는 것) logging이 수행됨
Logger 클래스의 각 인스턴스에는 이름(__name__)이 있으며 마침표를 구분 기호로 사용하여 이름, 공간 계층 구조로 배열됩니다. 예를 들어 scan.text, scan.html, scan.pdf Logger의 부모는 scan이라는 Logger임
logger = logging.getLogger(__name__)
Logger의 이름을 지을 때 사용하는 좋은 규칙은 logging을 사용하는 각 모듈에서 묘듈 수준 Logger를 사용하는 것. 위와 같은 <logger = logging.getLogger(__name__)>명령은 Logger의 이름(__name__)이 패키지/모듈 계층을 추적한다는 것을 의미하고, Logger 이름으로부터 event가 기록되는 위치를 직관적으로 알 수 있음
위 코드에서 getLogger()는 이름이 제공되는 경우, 지정된 이름을 가진 logger 인스턴스에 대한 참조를 반환하고, 그렇지 않으면 root를 반환. 또한,이름이 foo인 logger가 주어지면, foo.scr, foo.bar, foo.bar.baz 의 이름을 가진 logger는 모두 foo의 자손
Logger의 역할을 보면,
응용 프로그램이 실행시간에 메세지를 기록할 수 있도록 여러 메서드를 응용 프로그램 코드에 노출함
Logger 객체는 Levelname(debug, info, warning, error, critical)에 따라 어떤 log message를 처리할지 결정
또한 관련 log message를 관심 있는 모든 log handler로 전달
Handler?
Handler 객체는 적절한 log message를 handler의 지정된 대상으로 전달하는 역할
Logger 객체는 addHandler()라는 메서드를 사용하여 handler 객체를 추가
예를 들어, 응용 프로그램이 모든 log message를 log file로 보내고, error와 그 이상의 모든 log message를 표준 출력으로 보내고, critical 메세지를 전자 메일 주소로 보냄
( Handler 객체가 하는 일은 debug, info, warning, error, critical와 같은 logging수준에 따라 출력되는 log message를, handler의 지정된 대상, 즉 log file이나 전자 메일 주소 등으로 보낼 수 있다는 것)
유용한 Handler 정리
StreamHandler | Stream(파일류 객체)에 message를 보냄 |
FileHandler | 디스크 파일에 message를 보냄 |
SocketHandler | TCP/IP 소켓에 메시지를 보냄 |
DatagramHandler | UDP 소켓에 메시지를 보냄 |
SMTPHandler | 지정된 전자 우편 주소로 메시지를 보냄 |
SysLogHandler | 유닉스 syslog 데몬(원격 기계에 있는 것도 가능)에 메시지를 보냄 |
NTEventLogHandler | 윈도우 NT/2000/XP 이벤트 로그에 메시지를 보냄 |
MemoryHandler | 메모리에 있는 버퍼에 메시지를 보내는데, 특정 기준이 만족 될 때마다 플러시 됨 |
HTTPHandler | GET 또는 POST 을 사용해서 HTTP 서버에 메시지를 보냄 |
WatchedFileHandler | logging하고 있는 파일을 감시. 파일이 변경되면 닫히고 파일 이름을 사용하여 다시 열림. 이 처리기는 유닉스 계열 시스템에서만 유용 |
QueueHandler | queue 또는 multiprocessing 모듈에 구현된 것과 같은 큐로 메시지를 보냄 |
NullHandler |
에러 메시지로 아무것도 하지 않음. 라이브러리 개발자가 로깅을 사용하지만, 라이브러리 사용자가 로깅을 구성하지 않으면 표시될 수 있는 〈No handlers could be found for logger XXX〉 라는 메시지를 피하려고 할 때 사용 |
Formatter?
Formatter 객체는 log message의 최종 순서, 구조 및 내용을 구성
위에서 본 "ex) logging format"에서 나온 바와 같이
ex) logging format
import logging
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
logging.debug('This message should appear on the console')
logging.info('Hellow')
logging.warning('World')
Console-------------------------------------------------------------
INFO: Hellow
WARNING: World
basicConfig() 함수의 format인자에서 Formatter 객체를 사용한 것을 볼 수 있음
프로그래밍할 때의 logging 구성?
파이썬에서 제공하는 logging-tutorial 먼저보기
import logging
# logger 생성
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)
# handler(처리기) 생성 후 logging 수준을 debug로 설정
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# formatter 생성
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to ch (생성한 formatter를 이용해 handler에 추가함 = log message 포멧을 설정멧을 설정)
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message').
Console----------------------------------------------------------------------------------
$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message
Formatter에서 설정한 포멧대로 출력된 것을 볼 수 있음.
StreamHandler는 거의 모든 파일류 객체에 log message 출력을 보내는 handler임.
그리고 이름을 simple_example로 설정한 logger의 출력임을 알 수 있음.
- 구성 메서드를 호출하는 파이썬 코드를 사용하여 로거, 처리기 및 포매터를 명시적으로 만듬
- 로깅 구성 파일을 만들고, fileConfig() 함수를 사용하여 그것을 읽음
- 구성 정보의 딕셔너리를 만들고, dictConfig() 함수에 전달
logging.conf 라는 logging 구성 파일을 이용
import logging
import logging.config
logging.config.fileConfig('logging.conf')
# create logger
logger = logging.getLogger('simpleExample')
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')
Console-----------------------------------------------------------------------------------
$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message
2005-03-19 15:38:55,979 - simpleExample - INFO - info message
2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message
2005-03-19 15:38:56,055 - simpleExample - ERROR - error message
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message
# logging.conf 파일 내용-----------------------------------------------------------------------
[loggers]
keys=root,simpleExample
[handlers]
keys=consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=