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


109

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

내가 아는 것은 다음과 같습니다.

  1. 함수 이름을 얻기 위해 사용할 수는 function_name.__name__있지만 function_name을 사용하고 싶지 않습니다 ( Log.info("Message")모든 함수의 본문에 제네릭 을 빠르게 복사하여 붙여 넣을 수 있음 ). 나는 이것이 __func__매크로를 사용하여 C에서 할 수 있다는 것을 알고 있지만 파이썬에 대해서는 잘 모르겠습니다.

  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. 로깅을 사용하고 파일에 로깅하기 위해 처리기를 추가하는 방법을 알고 있지만 단일 파일을 사용하여 모든 로그 메시지를 프로젝트에서 함수 호출의 올바른 순서로 기록 할 수 있는지 확실하지 않습니다.

도움을 주시면 대단히 감사하겠습니다.

감사!


를 사용하여 Python 디버거로 이동 한 import pdb; pdb.set_trace()다음 대화식으로 코드를 단계별 로 실행할 수 있습니다 . 프로그램 흐름을 추적하는 데 도움이 될 수 있습니다.
매튜 Schinckel

좋은 생각입니다! 고마워 Matt. 매번 디버그 할 필요가 없도록 질문에서 언급 한대로 로그를 얻는 것이 여전히 도움이 될 것입니다. 또한 Java 용 Eclipse만큼 좋은 Python 용 IDE (Ctrl + 클릭으로 함수 정의로 이동)를 알고 있습니까? 디버깅을 더 쉽게 만드는 데 사용할 수 있습니까?
user1126425

답변:


28

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

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

다음 단계 : (2). locals()현재 범위의 사전을 제공합니다. 따라서 다른 인수가없는 메서드 self에는 현재 인스턴스에 대한 참조가 포함 된 범위가 있습니다. 당신을 괴롭히는 트릭은 %연산자 의 RHS로 dict를 사용하는 문자열 형식화 입니다. "%(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()프로그램을 다시 실행하여 언제든지 대화 형 디버깅 프롬프트 로 이동할 수도 있습니다 . 이를 통해 코드를 단계별로 진행하여 선택한 데이터를 검사 할 수 있습니다.


감사합니다 Matt! 이 자동 로그 기능을 사용해 보겠습니다. % 연산자의 RHS로 dict를 사용하는 것과 관련하여 약간의 혼란 '%(foo)s : %(bar)s'이 있습니다. bar["foo"]'s 값 도 인쇄 할까요? 아니면 귀하의 예와 다소 다른가요?
user1126425

기본적으로 양식의 모든 %(<foo>)s것은 dict에서 참조하는 객체의 값으로 대체됩니다 <foo>. 더 많은 예제 /에서 세부 사항이 있습니다 docs.python.org/library/stdtypes.html#string-formatting는
매튜 Schinckel

3
@synthesizerpatel의 답변이 훨씬 더 유용합니다.

504

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

import logging
logger = logging.getLogger('root')
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>]]

61
이것이 답이되었을 것입니다!
user3885927 2014 년

1
좋아요 .. 추가 할 한 가지는 로그 파일의 이름을 코드 파일과 동적으로 동일하게 지정할 수 있습니까? 예 : 동적으로 파일 이름을 가져 오기 위해 logging.basicConfig (filename = "% (filename)", format = FORMAT) 시도했지만 정적 값을 사용했습니다. 어떠한 제안?
Outlier

2
@Outlier 아니, 그것을 달성하기 위해 권장되는 방법은 경유getLogger(__name__)
farthVader

2
한 가지 질문이 있습니다. Java 어딘가에서 로거가 호출되는 줄을 파악하는 데 추가 시간이 걸리기 때문에 줄 번호를 인쇄하는 것이 권장되지 않는다는 것을 읽었습니다. 파이썬에서 이것은 사실이 아닙니다?
McSonk

2
관련성이 없지만 logging.getLogger('root')예상했던 것과는 다릅니다 . root로거가 아니라 이름이 'root'인 일반 로거입니다.
0xc0de

5

funcname, linenamelineno로깅을했던 마지막 함수에 대한 정보를 제공합니다.

로거 래퍼 (예 : 싱글 톤 로거)가있는 경우 @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)

1
귀하의 대답은 내 문제를 해결하는 데 필요한 것입니다. 감사합니다.
오류-Syntactical Remorse

파이썬 3.8 이후, logging클래스가 지원하는 스택 수준은 아웃 - 오브 - 박스 건너 뛰는 : 방법이 좋아 log(), debug()이제 동의 등의 stacklevel인수를. 문서를 참조하십시오 .
본편
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.