출처: http://gyus.me/?p=418 / https://inma.tistory.com/136
파이썬 표준 라이브러리인 logging을 활용하여 로그를 남기는 방법에 대해 알아봅니다.
[방법1] stream에 로그 남기기
- 스트림(콘솔)에 로그를 찍기 위해 logging을 사용합니다.
import logging
logging.info('my INFO log')
logging.warning('my WARNING log')
# WARNING:root:my WARNING log
위 코드를 실행하면 warning level만 출력되는 이유?
- logging의 default log level이 warning으로 되어있기 때문입니다.
다음 처럼 logging의 basicConfig level을 변경하면 됩니다.
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug('my DEBUG log')
logging.info('my INFO log')
logging.warning('my WARNING log')
logging.error('my ERROR log')
logging.critical('my CRITICAL log')
# DEBUG:root:my DEBUG log
# INFO:root:my INFO log
# WARNIG:root:my WARNING log
# ERROR:root:my ERROR log
# CRITICAL:root:my CRITICAL log
[방법2] file에 로그 남기기
- basicConfig의 filename을 설정하면 해당 위체 로그 파일이 생성됩니다.
- 해당 파일을 열어보면 log가 append 되는 것을 확인할 수 있습니다.
import logging
logging.basicConfig(filename='./server.log', level=logging.DEBUG)
logging.debug('my DEBUG log')
logging.info('my INFO log')
logging.warning('my WARNING log')
logging.error('my ERROR log')
logging.critical('my CRITICAL log')
# DEBUG:root:my DEBUG log
# INFO:root:my INFO log
# WARNIG:root:my WARNING log
# ERROR:root:my ERROR log
# CRITICAL:root:my CRITICAL log
# servevr.log
DEBUG:root:my DEBUG log
INFO:root:my INFO log
WARNIG:root:my WARNING log
ERROR:root:my ERROR log
CRITICAL:root:my CRITICAL log
[방법3] stream과 file에 동시에 로그 남기기
1. logging.getLogger(__name__)으로 logger instance를 생성합니다.
2. stream과 file에 로그를 남기는 handler를 생성합니다.
3. logger instance에 stream과 file handler를 설정합니다.
4. logger instance로 log를 찍습니다.
import logging
# logger instance 생성
logger = logging.getLogger(__name__)
# handler 생성 (stream, file)
streamHandler = logging.StreamHandler()
fileHandler = logging.FileHandler('./server.log')
# logger instance에 handler 설정
logger.addHandler(streamHandler)
logger.addHandler(fileHandler)
# logger instance로 log 찍기
logger.setLevel(level=logging.DEBUG)
logger.debug('my DEBUG log')
logger.info('my INFO log')
logger.warning('my WARNING log')
logger.error('my ERROR log')
logger.critical('my CRITICAL log')
로그 형식(formatting)
import logging
import logging.handlers
# logger instance 생성
logger = logging.getLogger(__name__)
# formatter 생성
formatter = logging.Formatter('[%(asctime)s][%(levelname)s|%(filename)s:%(lineno)s] >> %(message)s')
# handler 생성 (stream, file)
streamHandler = logging.StreamHandler()
fileHandler = logging.FileHandler('./server.log')
# logger instance에 fomatter 설정
streamHandler.setFormatter(formatter)
fileHandler.setFormatter(formatter)
# logger instance에 handler 설정
logger.addHandler(streamHandler)
logger.addHandler(fileHandler)
# logger instnace로 log 찍기
logger.setLevel(level=logging.DEBUG)
logger.debug('my DEBUG log')
logger.info('my INFO log')
logger.warning('my WARNING log')
logger.error('my ERROR log')
logger.critical('my CRITICAL log')
formatter를 적용하면 다음과 같이 출력되는 것을 확인하실 수 있습니다.
(더 다양한 포맷은 여기에서 확인하실 수 있습니다.)
[2019-01-12 00:11:01,045][DEBUG|test.py:24] >> my DEBUG log
[2019-01-12 00:11:01,046][INFO|test.py:25] >> my INFO log
[2019-01-12 00:11:01,046][WARNING|test.py:26] >> my WARNING log
[2019-01-12 00:11:01,046][ERROR|test.py:27] >> my ERROR log
[2019-01-12 00:11:01,046][CRITICAL|test.py:28] >> my CRITICAL log
로그 파일이 커질때 파일 분할
- 파일을 분할하기 위해서는 fileHandler 설정을 변경하면 됩니다.
- maxBytes: 파일 하나의 최대 바이트 수
- backupCount: 백업 파일 개수
(더 다양한 핸들러는 여기에서 확인하실 수 있습니다.)
import logging
# 100MB 파일을 10개까지 남기겠다라는 의미입니다.
fileMaxByte = 1024 * 1024 * 100
fileHandler = logging.handlers.RotatingFileHandler(filename, maxBytes=fileMaxByte, backupCount=10)
눈치가 빠른 사람이라면 `logging.handlers` 아래에 다른 핸들러들도 많겠구나~ 라는 생각이들것이다.
[logging.handlers](https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers) 링크를 타고 가보면 많은 핸들러들을 볼 수가 있다.
어지간한 기능은 다 넣어본것 같은데 기존에 없는 기능을 추가할려면 어떻게 하지?!
### 확장이 쉬우면 좋겠다!
에러가 났을 때 mongodb에 그 정보를 저장했으면 좋겠다! 어떻게 하지?
일단 쉬운 방법은 나보다 똑똑한 사람이 만들어 놓은 것을 쓰면 된다. 요즘 세상이 참 좋은 세상이라 구글로 찾으면 내가 생각한건 다있다. ㅎㅎ 근데 가끔 이렇게 찾아도 내 마음에 쏙~ 안들 수도 있다. 그럴 때는 한번 만들어보는 것도 힘들긴 하지만, 도움이 될 때가 많다.
그런 의미에서 다른분들도 이미 뜬 삽이겠지만, 나도 한삽을 더 해보려고 한다. 진짜 기본기능만 되는걸 하나 만들어보자.
참고로, mongodb 모듈로 pymongo가 설치되어 있어야 한다. `pip3 install pymongo`로 간단히 설치가능하다.
mongodb도 물론 설치가 되어있어야한다. 해당 내용은 이글과는 크게 관계없으므로 생략하겠다.
핸들러를 만드는 순서는 아래와 같다.
1. mongodb에 로그를 저장할 수 있도록 handler를 만든다.
2. handler는 logging.Handler를 상속하고 emit 메서드를 구현하면된다.
간단히 만들어본 소스는 아래와 같다.
import logging
from pymongo.connection import Connection
from bson import InvalidDocument
class MongoHandler(logging.Handler):
def __init__(self, db='mongolog', collection='log', host='localhost', port=None, level=logging.NOTSET):
logging.Handler.__init__(self, level)
self.collection = Connection(host, port)[db][collection]
def emit(self, record):
data = record.__dict__.copy()
try:
self.collection.save(data)
except InvalidDocument as e:
logging.error("Unable save log to mongodb: %s", e.message)
if __name__ == '__main__':
MongoHandler('mongolog', 'test')
테스트용 소스도 만들어보자. 간단히 핸들러를 추가하고 로그를 찍어본다.
import logging
from mongoLogger import MongoHandler
if __name__ == '__main__':
logger = logging.getLogger('mongoTest')
logger.setLevel(logging.WARNING)
logger.addHandler(MongoHandler('mongolog', 'log'))
logger.debug("test debug")
logger.info("test info")
logger.warning("test warning")
logger.error("test error")
logger.critical("test critical")
실행 후 mongodb에 들어가서 확인을 해보면 아래와 같이 WARNING이상의 로그가 저장되어 있다.
> db.log.find().pretty();
{
"_id" : ObjectId("5405c2cc1626051dcf238cfa"),
"stack_info" : null,
"exc_text" : null,
"exc_info" : null,
"processName" : "MainProcess",
"lineno" : 11,
"msecs" : 891.3910388946533,
"relativeCreated" : 50.26507377624512,
"process" : 7631,
"name" : "mongoTest",
"pathname" : "mongoTest.py",
"created" : 1409663692.891391,
"filename" : "mongoTest.py",
"funcName" : "",
"threadName" : "MainThread",
"msg" : "test warning",
"args" : [ ],
"module" : "mongoTest",
"levelno" : 30,
"thread" : NumberLong("140735296762640"),
"levelname" : "WARNING"
}
{
"_id" : ObjectId("5405c2cc1626051dcf238cfb"),
"stack_info" : null,
"exc_text" : null,
"exc_info" : null,
"processName" : "MainProcess",
"lineno" : 12,
"msecs" : 891.618013381958,
"relativeCreated" : 50.492048263549805,
"process" : 7631,
"name" : "mongoTest",
"pathname" : "mongoTest.py",
"created" : 1409663692.891618,
"filename" : "mongoTest.py",
"funcName" : "",
"threadName" : "MainThread",
"msg" : "test error",
"args" : [ ],
"module" : "mongoTest",
"levelno" : 40,
"thread" : NumberLong("140735296762640"),
"levelname" : "ERROR"
}
{
"_id" : ObjectId("5405c2cc1626051dcf238cfc"),
"stack_info" : null,
"exc_text" : null,
"exc_info" : null,
"processName" : "MainProcess",
"lineno" : 13,
"msecs" : 891.7689323425293,
"relativeCreated" : 50.642967224121094,
"process" : 7631,
"name" : "mongoTest",
"pathname" : "mongoTest.py",
"created" : 1409663692.891769,
"filename" : "mongoTest.py",
"funcName" : "",
"threadName" : "MainThread",
"msg" : "test critical",
"args" : [ ],
"module" : "mongoTest",
"levelno" : 50,
"thread" : NumberLong("140735296762640"),
"levelname" : "CRITICAL"
}
### 결론
파이썬에서는 로그를 남기기 위해서 뭘쓸까 고민할 필요가 전혀 없다. 표준 라이브러리가 워낙에 잘되어 있고, 확장 또한 쉽기 때문에 별다른 고민없이 `logging` 모듈만 잘 공부하면 된다. 나도 필요해서 찾아보고 공부해본 것이지만, 위에서 소개한 것 이외에도 많은 기능들을 가지고 있으므로 아마 거의 대부분의 경우에는 표준 logging모듈로도 충분할 것으로 생각된다.
관심이 있는 사람은 [logging-cookbook](https://docs.python.org/3/howto/logging-cookbook.html#logging-cookbook) 페이지를 참고하도록 하자.
'언어 > Python' 카테고리의 다른 글
PC / RPi camera display using PyQt and OpenCV (0) | 2019.11.18 |
---|---|
PyQt serial terminal (0) | 2019.11.18 |
Windows에서 PyCharm을 사용하여 Python2와 Python3 동시에 사용하기 (0) | 2018.08.29 |
Windows에 Python2, Python3 설치 하는 방법 (0) | 2018.08.29 |
RPi_GPIO_Code_Samples (0) | 2018.08.26 |