[Python] python logging module 활용

python logging module 사용

1. basic config 사용

  • ref: https://realpython.com/python-logging/
  • logging config 쉽게 설정가능(설정가능한 param: link)
  • 대개 level, filename, filemode, format, 설정
    • filemode 기본은 a append 모드로, 같은 파일에 계속 연이어서 작성.
    • filemode, format 등 상세 항목은 링크참조(filemode, format)
1
2
3
logging.basicConfig(\
level=logging.DEBUG, filename='app.log', filemode='w', \
ormat='%(name)s - %(levelname)s - %(message)s')
  • 이후 상황에 맞게 아래 코드 삽입
1
2
3
4
5
6
7
8
9
10
11
12
13
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

>> app.log
# format='%(name)s - %(levelname)s - %(message)s'에 맞게 저장됨
root - DEBUG - This is a debug message
root - INFO - This is an info message
root - WARNING - This is a warning message
root - ERROR - This is an error message
root - CRITICAL - This is a critical message

2. custom logger 사용

  • 우선 logger obj를 생성
  • handler, formatter, level등을 설정
    • handlerStreamHandler, FileHandler, SMTPHandler, HTTPHandler 등이 있음(link)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# logger 설정
import logging

name = 'new1'
logger = logging.getLogger(name)

# output format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# stream handler
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.DEBUG)
stream_handler.setFormatter(formatter)

# file handler
file_handler = logging.FileHandler('./my.log', mode='w')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)

# add handlers
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 실제적용
for i in range(10):
if i % 50 == 0:
try:
8 % 0
except Exception as e:
logger.error('raise exception', exc_info=True)
logger.info('test info: {}'.format(i))

>> output
2019-10-12 05:29:16,318 - new1 - ERROR - raise exception
Traceback (most recent call last):
File "<ipython-input-153-785ef163e305>", line 24, in <module>
8 % 0
ZeroDivisionError: integer division or modulo by zero
2019-10-12 05:29:16,320 - new1 - INFO - test info: 0
2019-10-12 05:29:16,321 - new1 - INFO - test info: 1
2019-10-12 05:29:16,323 - new1 - INFO - test info: 2
2019-10-12 05:29:16,324 - new1 - INFO - test info: 3
2019-10-12 05:29:16,325 - new1 - INFO - test info: 4
2019-10-12 05:29:16,326 - new1 - INFO - test info: 5
2019-10-12 05:29:16,328 - new1 - INFO - test info: 6
2019-10-12 05:29:16,329 - new1 - INFO - test info: 7
2019-10-12 05:29:16,330 - new1 - INFO - test info: 8
2019-10-12 05:29:16,331 - new1 - INFO - test info: 9

주의: 핸들러 중복 추가

  • ref: https://stackoverflow.com/questions/42700037/python-logging-in-several-modules-outputs-twice
  • 하기 코드에서 logger = get_logger(__name__)를 n번 시행하면, get_logger(name) 함수 내부의 addHandler를 n번 시행하게 됨. 따라서 console에 print out되는 횟수가 n번
  • 때문에 같은 이름의 logger generate는 한 번만 할 것. (get_logger한번만 시행)
  • 또는 handler추가되는 것 방지하는 코드 삽입(두번째 코드)
1
2
3
4
5
6
7
8
9
10
11
12
# 1st code
# handdler가 계속 추가되는 경우
def get_logger(name):
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
console = logging.StreamHandler()
logger.addHandler(console)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(message)s')
console.setFormatter(formatter)
return logger

logger = get_logger(__name__) # logger를 call할 때마다, 기존 logger에 handler 추가됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2nd code
def get_logger(name):
logger = logging.getLogger(name)
logger.handlers.clear() # 생성할 때마다, 해당 logger의 handler 초기화
logger.setLevel(logging.INFO)
if not logger.handlers: # 핸들러가 없을 때만, 신규 핸들러 추가
#Prevent logging from propagating to the root logger
#logger.propagate = True

console = logging.StreamHandler()
logger.addHandler(console)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(message)s')
console.setFormatter(formatter)
return logger

3. 실무 사용 예제코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def get_logger(self):
# init
name = 'crawler'
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
formatter = logging.Formatter\
('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# handler: console
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(formatter)

# handler: file
timestr = time.strftime('%Y%m%d_%H:%M:%S')
file_name = './crawler_{}.log'.format(timestr)

file_handler = logging.FileHandler(file_name)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)

# wrap
logger.handlers.clear()
logger.addHandler(console)
logger.addHandler(file_handler)
return logger
< !-- add by yurixu 替换Google的jquery并且添加判断逻辑 -->