Python 로그 출력 에 상하 문 정보 추가
앞에서 언급 했 듯 이 로그 기록 함수 에 extra 인 자 를 전달 하여 로그 출력 에 추가 적 인 컨 텍스트 정 보 를 추가 할 수 있 습 니 다.예 를 들 어:
import logging
import sys
fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s")
h_console = logging.StreamHandler(sys.stdout)
h_console.setFormatter(fmt)
logger = logging.getLogger("myPro")
logger.setLevel(logging.DEBUG)
logger.addHandler(h_console)
extra_dict = {"ip": "113.208.78.29", "username": "Petter"}
logger.debug("User Login!", extra=extra_dict)
extra_dict = {"ip": "223.190.65.139", "username": "Jerry"}
logger.info("User Access!", extra=extra_dict)
출력:2017-05-14 15:47:25,562 - myPro - 113.208.78.29 - Petter - User Login!
2017-05-14 15:47:25,562 - myPro - 223.190.65.139 - Jerry - User Access!
그러나 이런 방식 으로 정 보 를 전달 하 는 것 은 그리 편리 하지 않다.로그 기록 방법 을 호출 할 때마다 extra 키워드 인 자 를 전달 해 야 하기 때문이다.삽입 할 컨 텍스트 정보 가 없 더 라 도 이 logger 가 설정 한 formatter 형식 에서 지정 한 필드 가 존재 해 야 하기 때 문 입 니 다.그래서 저 희 는 아래 두 가지 방식 으로 문맥 정보의 도입 을 실현 하 는 것 을 추천 합 니 다.
연결 을 만 들 때마다 Logger 인 스 턴 스 를 만들어 서 위 에 존재 하 는 문 제 를 해결 하려 고 시도 할 수 있 지만 이것 은 좋 은 해결 방안 이 아 닙 니 다.이 Logger 인 스 턴 스 는 쓰레기 수 거 를 하지 않 기 때 문 입 니 다.비록 이것 은 실천 에서 문제 가 되 지 않 지만 Logger 의 수량 이 통제 할 수 없 게 되면 관리 하기 매우 어 려 울 것 이다.
2.LoggerAdapters 를 사용 하여 문맥 정 보 를 도입 합 니 다.
LoggerAdapter 클래스 를 사용 하여 컨 텍스트 정 보 를 로그 이벤트 의 정보 에 전달 하 는 것 은 매우 간단 한 방식 입 니 다.첫 번 째 실현 방식 의 최적화 판 으로 볼 수 있 습 니 다.extra 에 기본 값 을 제공 하기 때 문 입 니 다.이 디자인 은 Logger 와 유사 하기 때문에 Logger 류 의 인 스 턴 스 를 사용 하 는 것 처럼 debug(),info(),warning(),error(),exception(),critical(),log()방법 을 호출 할 수 있 습 니 다.Logger Adapter 의 인 스 턴 스 를 만 들 때 Logger 인 스 턴 스 와 컨 텍스트 정 보 를 포함 하 는 사전 대상 이 이러한 인 스 턴 스 구축 방법 을 전달 해 야 합 니 다.LoggerAdapter 인 스 턴 스 의 로그 기록 방법 을 호출 할 때 이 방법 은 로그 로그 로그 메시지 와 사전 대상 을 처리 한 후 이 인 스 턴 스 를 구축 할 때 이 인 스 턴 스 의 logger 대상 에 게 전달 하 는 동명 의 로그 기록 방법 을 호출 합 니 다.다음은 LoggerAdapter 클래스 의 몇 가지 방법 에 대한 정의 입 니 다.
class LoggerAdapter(object):
"""
An adapter for loggers which makes it easier to specify contextual
information in logging output.
"""
def __init__(self, logger, extra):
"""
Initialize the adapter with a logger and a dict-like object which
provides contextual information. This constructor signature allows
easy stacking of LoggerAdapters, if so desired.
You can effectively pass keyword arguments as shown in the
following example:
adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
"""
self.logger = logger
self.extra = extra
def process(self, msg, kwargs):
"""
Process the logging message and keyword arguments passed in to
a logging call to insert contextual information. You can either
manipulate the message itself, the keyword args or both. Return
the message and kwargs modified (or not) to suit your needs.
Normally, you'll only need to override this one method in a
LoggerAdapter subclass for your specific needs.
"""
kwargs["extra"] = self.extra
return msg, kwargs
def debug(self, msg, *args, **kwargs):
"""
Delegate a debug call to the underlying logger, after adding
contextual information from this adapter instance.
"""
msg, kwargs = self.process(msg, kwargs)
self.logger.debug(msg, *args, **kwargs)
위의 코드 를 분석 함으로써 다음 과 같은 결론 을 얻 을 수 있다.
import logging
import sys
# LoggerAdapter logger
fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s")
h_console = logging.StreamHandler(sys.stdout)
h_console.setFormatter(fmt)
init_logger = logging.getLogger("myPro")
init_logger.setLevel(logging.DEBUG)
init_logger.addHandler(h_console)
# LoggerAdapter
extra_dict = {"ip": "IP", "username": "USERNAME"}
# LoggerAdapter
logger = logging.LoggerAdapter(init_logger, extra_dict)
#
logger.info("User Login!")
logger.info("User Login!", extra={"ip": "113.208.78.29", "username": "Petter"})
logger.extra = {"ip": "113.208.78.29", "username": "Petter"}
logger.info("User Login!")
logger.info("User Login!")
출력 결과:
# extra :{"ip": "IP", "username": "USERNAME"}
2017-05-14 17:23:15,148 - myPro - IP - USERNAME - User Login!
# info(msg, extra) extra
2017-05-14 17:23:15,148 - myPro - IP - USERNAME - User Login!
# extra
2017-05-14 17:23:15,148 - myPro - 113.208.78.29 - Petter - User Login!
2017-05-14 17:23:15,148 - myPro - 113.208.78.29 - Petter - User Login!
위의 프로그램 출력 결과 에 따라 우 리 는 문 제 를 발견 할 수 있 습 니 다.LoggerAdapter 류 구조 방법 에 전 달 된 extra 매개 변수 값 은 LoggerAdapter 인 스 턴 스 의 로그 기록 함수(예 를 들 어 위 에서 호출 한 info()방법)의 extra 매개 변수 에 덮어 쓸 수 없습니다.LoggerAdapter 인 스 턴 스 의 extra 속성 을 수정 하여 기본 값(예 를 들 어 위 에서 사용 한 logger.extra=xxx)을 수정 할 수 있 습 니 다.그러나 이것 은 기본 값 이 수정 되 었 다 는 것 을 의미한다.이 문 제 를 해결 하 는 방향 은 LoggerAdapter 의 하위 클래스 를 실현 하고 process()방법 을 다시 쓰 는 것 이다.그 중에서 kwargs 매개 변수 에 대한 작업 은 그 자체 에 extra 키 워드 를 포함 하고 있 는 지 여 부 를 먼저 판단 해 야 합 니 다.포함 되 어 있 으 면 기본 값 으로 교체 하지 않 습 니 다.kwargs 인자 에 extra 키 가 포함 되 어 있 지 않 으 면 기본 값 을 가 져 옵 니 다.구체 적 인 실현 을 살 펴 보 자.
import logging
import sys
class MyLoggerAdapter(logging.LoggerAdapter):
def process(self, msg, kwargs):
if 'extra' not in kwargs:
kwargs["extra"] = self.extra
return msg, kwargs
if __name__ == '__main__':
# LoggerAdapter logger
fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s")
h_console = logging.StreamHandler(sys.stdout)
h_console.setFormatter(fmt)
init_logger = logging.getLogger("myPro")
init_logger.setLevel(logging.DEBUG)
init_logger.addHandler(h_console)
# LoggerAdapter
extra_dict = {"ip": "IP", "username": "USERNAME"}
# LoggerAdapter
logger = MyLoggerAdapter(init_logger, extra_dict)
#
logger.info("User Login!")
logger.info("User Login!", extra={"ip": "113.208.78.29", "username": "Petter"})
logger.info("User Login!")
logger.info("User Login!")
출력 결과:
# extra :{"ip": "IP", "username": "USERNAME"}
2017-05-22 17:35:38,499 - myPro - IP - USERNAME - User Login!
# info(msg, extra) extra
2017-05-22 17:35:38,499 - myPro - 113.208.78.29 - Petter - User Login!
# extra
2017-05-22 17:35:38,499 - myPro - IP - USERNAME - User Login!
2017-05-22 17:35:38,499 - myPro - IP - USERNAME - User Login!
OK! 문제 가 해결 되 었 다.사실 formatter 의 제한 을 받 지 않 고 로그 출력 에 자 유 롭 게 필드 를 삽입 하려 면 LoggerAdapter 의 하위 클래스 를 사용자 정의 하 는 process()방법 으로 사전 매개 변수 에 있 는 키워드 정 보 를 로그 이벤트 메시지 에 연결 할 수 있 습 니 다.이 컨 텍스트 의 필드 정보 가 로그 출력 에서 의 위 치 는 제한 되 어 있 음 이 분명 합 니 다.'extra'를 사용 하 는 장점 은 이러한 사전 대상 의 값 이 이 LogRecord 인 스 턴 스 의 에 합 쳐 진 다 는 것 이다.dict__Formatter 인 스 턴 스 를 통 해 로그 출력 형식 문자열 을 사용자 정의 할 수 있 습 니 다.이 는 컨 텍스트 정보 에 있 는 필드 정보 가 로그 출력 에서 의 위 치 를 내 장 된 필드 와 같이 유연 하 게 만 들 지만 전 제 는 구조 기 방법 에 전달 되 는 이러한 사전 대상 의 key 가 확실 하고 명료 해 야 한 다 는 것 이다.
3.Filters 를 사용 하여 문맥 정 보 를 도입 합 니 다.
또한,사용자 정의 Filter.Filter 인 스 턴 스 방식 을 사용 하여 filter(record)방법 에서 전 달 된 LogRecord 인 스 턴 스 를 수정 하고 추가 할 컨 텍스트 정 보 를 새로운 속성 으로 할당 할 수 있 습 니 다.그러면 formatter 의 문자열 형식 을 지정 하여 컨 텍스트 정 보 를 출력 할 수 있 습 니 다.
저 희 는 위의 실현 을 모방 하여 filter(record)방법 을 전달 하 는 LogRecord 인 스 턴 스 에 현재 네트워크 요청 과 관련 된 두 가지 정 보 를 추가 합 니 다:ip 와 username.
import logging
from random import choice
class ContextFilter(logging.Filter):
ip = 'IP'
username = 'USER'
def filter(self, record):
record.ip = self.ip
record.username = self.username
return True
if __name__ == '__main__':
levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
users = ['Tom', 'Jerry', 'Peter']
ips = ['113.108.98.34', '219.238.78.91', '43.123.99.68']
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)-15s %(name)-5s %(levelname)-8s %(ip)-15s %(username)-8s %(message)s')
logger = logging.getLogger('myLogger')
f = ContextFilter()
logger.addFilter(f)
logger.debug('A debug message')
logger.info('An info message with %s', 'some parameters')
for x in range(5):
lvl = choice(levels)
lvlname = logging.getLevelName(lvl)
filter.ip = choice(ips)
filter.username = choice(users)
logger.log(lvl, 'A message at %s level with %d %s', lvlname, 2, 'parameters')
출력 결과:2017-05-15 10:21:49,401 myLogger DEBUG IP USER A debug message
2017-05-15 10:21:49,401 myLogger INFO IP USER An info message with some parameters
2017-05-15 10:21:49,401 myLogger INFO 219.238.78.91 Tom A message at INFO level with 2 parameters
2017-05-15 10:21:49,401 myLogger INFO 219.238.78.91 Peter A message at INFO level with 2 parameters
2017-05-15 10:21:49,401 myLogger DEBUG 113.108.98.34 Jerry A message at DEBUG level with 2 parameters
2017-05-15 10:21:49,401 myLogger CRITICAL 43.123.99.68 Tom A message at CRITICAL level with 2 parameters
2017-05-15 10:21:49,401 myLogger INFO 43.123.99.68 Jerry A message at INFO level with 2 parameters
설명 이 필요 한 것 은 실제 네트워크 프로그램 에 서 는 다 중 스 레 드 병행 시의 스 레 드 보안 문 제 를 고려 해 야 할 수도 있 습 니 다.이 때 연결 정보 나 사용자 정의 필터 의 인 스 턴 스 를 threading.local 을 통 해 threadlocal 에 저장 할 수 있 습 니 다.
위 에서 말 한 것 은 소 편 이 소개 한 Python 이 로그 출력 에 문맥 정 보 를 추가 하 는 것 입 니 다.도움 이 되 셨 으 면 좋 겠 습 니 다.궁금 한 점 이 있 으 면 메 시 지 를 남 겨 주세요.소 편 은 바로 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
로마 숫자를 정수로 또는 그 반대로 변환그 중 하나는 로마 숫자를 정수로 변환하는 함수를 만드는 것이었고 두 번째는 그 반대를 수행하는 함수를 만드는 것이었습니다. 문자만 포함합니다'I', 'V', 'X', 'L', 'C', 'D', 'M' ; 문자열이 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.