Python 로그 출력 에 상하 문 정보 추가

로그 기록 함수 에 전달 되 는 인자(예 를 들 어 msg)를 제외 하고 로그 출력 에 추가 적 인 컨 텍스트 정 보 를 포함 하고 싶 을 때 도 있 습 니 다.예 를 들 어 한 네트워크 응용 프로그램 에서 로그 에 클 라 이언 트 의 특정한 정 보 를 기록 하고 싶 을 수도 있다.예 를 들 어 원 격 클 라 이언 트 의 IP 주소 와 사용자 이름 이다.여기 서 우 리 는 다음 과 같은 몇 가지 실현 방식 을 소개 한다.
  • 로그 기록 함수 에 extra 매개 변 수 를 전달 하여 문맥 정 보 를 도입 합 니 다
  • LoggerAdapters 를 사용 하여 상하 문 정보 도입
  • Filters 를 사용 하여 상하 문 정보 도입
  • 1.로그 기록 함수 에 extra 매개 변 수 를 전달 하여 문맥 정 보 를 도입 합 니 다.
    앞에서 언급 했 듯 이 로그 기록 함수 에 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)
    위의 코드 를 분석 함으로써 다음 과 같은 결론 을 얻 을 수 있다.
  • 컨 텍스트 정 보 는 LoggerAdapter 류 의 process()방법 에 로그 에 기 록 된 출력 메시지 에 추 가 된 것 입 니 다.사용자 정의 수 요 를 실현 하려 면 LoggerAdapter 의 하위 클래스 를 실현 하고 process()방법 을 다시 쓰 면 됩 니 다.
  • process()방법의 기본 구현 에서 msg 의 값 을 수정 하지 않 고 키워드 매개 변수 에 extra 라 는 key 를 삽입 하 였 습 니 다.이 extra 의 값 은 LoggerAdapter 류 구조 방법 에 전달 하 는 매개 변수 값 입 니 다.
  • LoggerAdapter 류 구축 방법 에 접 수 된 extra 매개 변 수 는 실제 적 으로 logger 의 formatter 형식 요 구 를 만족 시 키 기 위 한 기본 컨 텍스트 정보 입 니 다.
  • 위 에서 언급 한 세 번 째 결론 에 대해 우 리 는 예 를 들 어 보 자.
    
    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 이 로그 출력 에 문맥 정 보 를 추가 하 는 것 입 니 다.도움 이 되 셨 으 면 좋 겠 습 니 다.궁금 한 점 이 있 으 면 메 시 지 를 남 겨 주세요.소 편 은 바로 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

    좋은 웹페이지 즐겨찾기