source

단일 파일을 사용한 Python Logging(함수 이름, 파일 이름, 줄 번호)

factcode 2023. 6. 23. 22:28
반응형

단일 파일을 사용한 Python Logging(함수 이름, 파일 이름, 줄 번호)

저는 애플리케이션이 어떻게 작동하는지 배우려고 노력하고 있습니다.그리고 이를 위해 각 함수 본문의 첫 번째 줄로 debug 명령을 삽입하여 함수의 이름과 로그 출력으로 메시지를 보내는 줄 번호(코드 내)를 기록합니다.마지막으로, 이 응용 프로그램은 많은 파일로 구성되어 있기 때문에 응용 프로그램의 제어 흐름을 더 잘 이해할 수 있도록 단일 로그 파일을 만들고 싶습니다.

제가 알고 있는 것은 다음과 같습니다.

  1. 함수 이름을 가져오는 데 사용할 수 있습니다.function_name.__name__하지만 일반 파일을 빠르게 복사하고 붙여넣을 수 있도록 function_name을 사용하고 싶지 않습니다.Log.info("Message")모든 함수의 본문에서).나는 이것이 C에서 할 수 있다는 것을 압니다.__func__매크로 하지만 파이썬에 대해서는 잘 모르겠습니다.

  2. 파일 이름과 줄 번호를 얻기 위해, 나는 내 애플리케이션이 파이썬을 사용하는 것을 보았다 (그리고 나는 믿는다).locals()기능을 하지만 내가 완전히 알지 못하는 구문으로. 예: options = "LOG.debug('%(flag)s : %(flag_get)s' % locals())그리고 다음과 같은 것을 사용해 보았습니다.LOG.info("My message %s" % locals())그것은 다음과 같은 것을 생산합니다.{'self': <__main__.Class_name object at 0x22f8cd0>}이것에 대한 의견이 있으십니까?

  3. 저는 로깅을 사용하는 방법을 알고 파일에 로그할 핸들러를 추가하지만 프로젝트에서 모든 로그 메시지를 함수 호출의 올바른 순서로 기록하는 데 단일 파일을 사용할 수 있는지는 잘 모르겠습니다.

정답은 이미 제공된 변수를 사용하는 것입니다.

import logging
logger = logging.getLogger(__name__)
FORMAT = "[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s"
logging.basicConfig(format=FORMAT)
logger.setLevel(logging.DEBUG)

원하는 곳에 다음을 추가하면 됩니다.

logger.debug('your message') 

현재 작업 중인 스크립트의 출력 예:

[invRegex.py:150 -          handleRange() ] ['[A-Z]']
[invRegex.py:155 -     handleRepetition() ] [[<__main__.CharacterRangeEmitter object at 0x10ba03050>, '{', '1', '}']]
[invRegex.py:197 -          handleMacro() ] ['\\d']
[invRegex.py:155 -     handleRepetition() ] [[<__main__.CharacterRangeEmitter object at 0x10ba03950>, '{', '1', '}']]
[invRegex.py:210 -       handleSequence() ] [[<__main__.GroupEmitter object at 0x10b9fedd0>, <__main__.GroupEmitter object at 0x10ba03ad0>]]

여기에 약간의 관련 질문이 있습니다.

가장 쉬운 것부터 시작하겠습니다: (3).사용.logging모든 호출을 단일 로그 파일 또는 다른 출력 대상으로 집계할 수 있습니다. 이는 프로세스에서 발생한 순서대로 이루어집니다.

다음은 (2)입니다.locals()현재 범위에 대한 딕트를 제공합니다.따라서, 다른 인수가 없는 방법에서, 당신은self범위: 현재 인스턴스에 대한 참조를 포함합니다.당신을 놀라게 하는 속임수는 딕트를 RHS로 사용하는 문자열 포맷입니다.%교환입니다. "%(foo)s" % bar의 가치가 무엇이든 간에 대체될 것입니다.bar["foo"]사실은.

마지막으로, 당신은 몇 가지 자기 검사 트릭을 사용할 수 있습니다.pdb더 많은 정보를 기록할 수 있습니다.

def autolog(message):
    "Automatically log the current function details."
    import inspect, logging
    # Get the previous frame in the stack, otherwise it would
    # be this function!!!
    func = inspect.currentframe().f_back.f_code
    # Dump the message + the name of this function to the log.
    logging.debug("%s: %s in %s:%i" % (
        message, 
        func.co_name, 
        func.co_filename, 
        func.co_firstlineno
    ))

전달된 메시지와 (원래) 함수 이름, 정의가 나타나는 파일 이름 및 해당 파일의 줄이 기록됩니다.검사 보기 - 활성 개체를 검사하여 자세한 내용을 확인합니다.

앞서 제 의견에서 언급했듯이, 당신은 또한 다음과 같은 것으로 빠질 수 있습니다.pdb든지 대화형 됩니다.import pdb; pdb.set_trace()프로그램을 다시 실행할 수 있습니다.이렇게 하면 선택한 대로 데이터를 검사하면서 코드를 단계적으로 수행할 수 있습니다.

funcname,linename그리고.lineno마지막으로 로깅을 수행한 함수에 대한 정보를 제공합니다.

로거 래퍼(예: 싱글톤 로거)가 있는 경우 @synthesizerpatel의 응답이 사용자에게 적합하지 않을 수 있습니다.

통화 스택에서 다른 발신자를 찾기 위해 다음을 수행할 수 있습니다.

import logging
import inspect

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class MyLogger(metaclass=Singleton):
    logger = None

    def __init__(self):
        logging.basicConfig(
            level=logging.INFO,
            format="%(asctime)s - %(threadName)s - %(message)s",
            handlers=[
                logging.StreamHandler()
            ])

        self.logger = logging.getLogger(__name__ + '.logger')

    @staticmethod
    def __get_call_info():
        stack = inspect.stack()

        # stack[1] gives previous function ('info' in our case)
        # stack[2] gives before previous function and so on

        fn = stack[2][1]
        ln = stack[2][2]
        func = stack[2][3]

        return fn, func, ln

    def info(self, message, *args):
        message = "{} - {} at line {}: {}".format(*self.__get_call_info(), message)
        self.logger.info(message, *args)

저는 @synthesizerpatel이 제공한 답변을 좋아하지만 레벨 이름을 포함하는 이 형식이 더 좋습니다.

FORMAT = "[%(asctime)s %(filename)s->%(funcName)s():%(lineno)s]%(levelname)s: %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO)

결과는 다음과 같습니다.

[2022-04-25 11:00:50,885 main.py->loop():21]INFO: looping

포인트 3의 경우 다음을 사용하여 파일에 로그할 수 있습니다.filename기본 구성:

logging.basicConfig(format=FORMAT, level=logging.INFO, filename='main.log')

하지만 RotatingFileHandler를 사용하여 로그 파일이 제어되지 않도록 로그 파일을 회전 설정하는 것을 선호합니다.로그 파일에 쓰는 동시에 콘솔에도 표시됩니다.

예제 체예main.py

import logging                                                                                         
from logging.handlers import RotatingFileHandler                                                       
import time                                                                                            
                                                                                                       
#Setup logger                                                                                          
logger = logging.getLogger(__name__)                                                                   
FORMAT = "[%(asctime)s %(filename)s->%(funcName)s():%(lineno)s]%(levelname)s: %(message)s"             
logging.basicConfig(format=FORMAT, level=logging.INFO)                                                 
#Log to file                                                                                           
logging_filename = 'main.log'                                                                          
handler = RotatingFileHandler(logging_filename, maxBytes=1000000, backupCount=10) #10 files of 1MB each
handler.setFormatter(logging.Formatter(FORMAT))                                                        
logger.addHandler(handler)                                                                             
                                                                                                       
def main():                                                                                            
  while True:                                                                                        
    loop()                                                                                         
    time.sleep(1)                                                                                  
                                                                                                       
def loop():                                                                                            
  logger.info('looping')                                                                             
                                                                                                       
if __name__== "__main__":                                                                              
  main()                                                                                               
                                                                                                       

언급URL : https://stackoverflow.com/questions/10973362/python-logging-function-name-file-name-line-number-using-a-single-file

반응형