Как я могу записать текущую строку и собрать информацию с Python? - PullRequest
19 голосов
/ 23 февраля 2011

У меня есть функция регистрации следующим образом.

logging.basicConfig(
    filename = fileName,
    format = "%(levelname) -10s %(asctime)s %(message)s",
    level = logging.DEBUG
)

def printinfo(string):
    if DEBUG:
        logging.info(string)

def printerror(string):
    if DEBUG:
        logging.error(string)
    print string

Мне нужно войти в номер строки, информацию о стеке.Например:

1: def hello():
2:    goodbye()
3:
4: def goodbye():
5:    printinfo()

---> Line 5: goodbye()/hello()

Как я могу сделать это с Python?

решено

def printinfo(string):
    if DEBUG:
        frame = inspect.currentframe()
        stack_trace = traceback.format_stack(frame)
        logging.debug(stack_trace[:-1])
    if LOG:
        logging.info(string)

дает мне эту информацию, которая именно то, что мне нужно.

DEBUG      2011-02-23 10:09:13,500 [
  '  File "/abc.py", line 553, in <module>\n    runUnitTest(COVERAGE, PROFILE)\n', 
  '  File "/abc.py", line 411, in runUnitTest\n    printinfo(string)\n']

Ответы [ 8 ]

19 голосов
/ 23 февраля 2011

Текущее имя функции, модуль и номер строки, которые вы можете сделать, просто изменив строку формата, чтобы включить их.

logging.basicConfig(
    filename = fileName,
    format = "%(levelname) -10s %(asctime)s %(module)s:%(lineno)s %(funcName)s %(message)s",
    level = logging.DEBUG
)

Большинство людей хотят использовать стек только при регистрации исключения, а модуль регистрации делает этоавтоматически, если вы позвоните logging.exception().Если вы действительно хотите получать информацию из стека в другое время, вам потребуется использовать модуль трассировки для извлечения необходимой вам дополнительной информации.

17 голосов
/ 23 февраля 2011
import inspect
import traceback

def method():
   frame = inspect.currentframe()
   stack_trace = traceback.format_stack(frame)
   print ''.join(stack_trace)

Используйте stack_trace [: - 1], чтобы избежать включения метода / printinfo в трассировку стека.

10 голосов
/ 24 февраля 2011

Начиная с Python 3.2, это можно упростить, передав флаг stack_info=True в журнал вызовов .Однако вам нужно будет использовать один из приведенных выше ответов для любой более ранней версии.

4 голосов
/ 31 октября 2014

Поздний ответ, ну да ладно.

Другое решение состоит в том, что вы можете создать свой собственный форматер с фильтром, как указано в документации здесь .Это действительно отличная функция, так как вам больше не нужно использовать вспомогательную функцию (и вам нужно размещать вспомогательную функцию везде, где вам нужна трассировка стека).Вместо этого пользовательский формат реализует его непосредственно в самих журналах.

import logging
class ContextFilter(logging.Filter):
    def __init__(self, trim_amount)
        self.trim_amount = trim_amount
    def filter(self, record):
        import traceback
        record.stack = ''.join(
            str(row) for row in traceback.format_stack()[:-self.trim_amount]
        )
        return True

# Now you can create the logger and apply the filter.
logger = logging.getLogger(__name__)
logger.addFilter(ContextFilter(5))

# And then you can directly implement a stack trace in the formatter.    
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s \n %(stack)s')

Примечание. В приведенном выше коде я обрезал последние 5 кадров стека.Это просто для удобства, поэтому мы не показываем стековые фреймы из самого пакета журналирования python (возможно, его также необходимо настроить для разных версий пакета журналирования)

3 голосов
/ 23 февраля 2011

Вот пример, который, я надеюсь, может вам помочь:

import inspect
import logging

logging.basicConfig(
    format = "%(levelname) -10s %(asctime)s %(message)s",
    level = logging.DEBUG
)

def test():

    caller_list = []
    frame = inspect.currentframe()
    this_frame = frame  # Save current frame.

    while frame.f_back:
        caller_list.append('{0}()'.format(frame.f_code.co_name))
        frame = frame.f_back

    caller_line = this_frame.f_back.f_lineno
    callers =  '/'.join(reversed(caller_list))

    logging.info('Line {0} : {1}'.format(caller_line, callers))

def foo():
    test()

def bar():
    foo()

bar()

Результат:

INFO       2011-02-23 17:03:26,426 Line 28 : bar()/foo()/test()
2 голосов
/ 23 февраля 2011

Используйте модуль traceback .

logging.error(traceback.format_exc())
1 голос
/ 11 марта 2019

Это основано на ответе @ mouad, но стало более полезным (IMO), включив на каждом уровне имя файла (но не его полный путь) и номер строки стека вызовов, и оставив этот стек в самом недавно вызванном-из (то есть НЕ в обратном порядке), потому что я так хочу это прочитать: -)

Каждая запись имеет file: line: func (), которая является той же последовательностью, что и обычная трассировка стека, но все ната же строка намного более компактна.

import inspect

def callers(self):
    caller_list = []
    frame = inspect.currentframe()
    while frame.f_back:
        caller_list.append('{2}:{1}:{0}()'.format(frame.f_code.co_name,frame.f_lineno,frame.f_code.co_filename.split("\\")[-1]))
        frame = frame.f_back
    callers =  ' <= '.join(caller_list)
    return callers

Вам может понадобиться добавить дополнительный f_back, если у вас есть какие-то промежуточные вызовы для создания текста журнала.

        frame = inspect.currentframe().f_back

Выводит примерно так:

file2.py:620:func1() <= file3.py:211:func2() <= file3.py:201:func3() <= main.py:795:func4() <= file4.py:295:run() <= main.py:881:main()

Мне нужна эта трассировка стека только в двух ключевых функциях, поэтому я добавляю вывод вызывающих в текст в вызове logger.debug (), например, htis:

logger.debug("\nWIRE: justdoit request -----\n"+callers()+"\n\n")
0 голосов
/ 23 февраля 2011

Посмотрите на модуль трассировки

>>> import traceback
>>> def test():
>>>     print "/".join( str(x[2]) for x in traceback.extract_stack() )
>>> def main():
>>>     test()
>>> main()
<module>/launch_new_instance/mainloop/mainloop/interact/push/runsource/runcode/<module>/main/test
...