Подтвердить что ты не робот

Как я могу отключить ведение журналов на Python, не комментируя их?

Сегодня я думал о проекте Python, который я написал примерно год назад, где я использовал logging довольно широко. Я помню, что мне пришлось прокомментировать много вызовов регистрации в сценариях с внутренним циклом (90% -ный код) из-за накладных расходов (hotshot указал, что это одно из моих самых больших узких мест).

Интересно, есть ли какой-нибудь канонический способ программно отключить протоколирование вызовов в приложениях Python без комментирования и раскола все время. Я бы подумал, что вы можете использовать проверку/перекомпиляцию или манипуляцию байт-кодом, чтобы сделать что-то подобное, а - только объекты кода, которые вызывают узкие места. Таким образом, вы можете добавить манипулятор в качестве этапа последующей компиляции и использовать централизованный файл конфигурации, например:

[Leave ERROR and above]
my_module.SomeClass.method_with_lots_of_warn_calls

[Leave WARN and above]
my_module.SomeOtherClass.method_with_lots_of_info_calls

[Leave INFO and above]
my_module.SomeWeirdClass.method_with_lots_of_debug_calls

Конечно, вы хотели бы использовать его экономно и, возможно, с гранулярностью для каждой функции - только для объектов кода, которые показали, что logging является узким местом. Кто-нибудь знает что-нибудь подобное?

Примечание.. Есть несколько вещей, которые делают это более трудным для выполнения в соответствии с динамической типизацией и поздним связыванием. Например, любые вызовы метода с именем debug могут быть обернуты с помощью if not isinstance(log, Logger). В любом случае, я предполагаю, что все мелкие детали могут быть преодолены либо с помощью джентльменского соглашения, либо с некоторой проверкой времени выполнения.: -)

4b9b3361

Ответ 1

Как насчет использования logging.disable?

Я также обнаружил, что мне пришлось использовать logging.isEnabledFor, если сообщение о регистрации очень дорого для создания.

Ответ 2

Я также видел утверждение, используемое таким образом.

assert logging.warn('disable me with the -O option') is None

(Я предполагаю, что предупреждение всегда возвращает none.. Если нет, вы получите AssertionError

Но на самом деле это просто забавный способ сделать это:

if __debug__: logging.warn('disable me with the -O option')

При запуске script с этой строкой в ​​нем с опцией -O строка будет удалена из оптимизированного .pyo-кода. Если вместо этого у вас была ваша собственная переменная, как и в следующем случае, у вас будет условие, которое всегда выполняется (независимо от того, какое значение имеет переменная), хотя условное выражение должно выполняться быстрее, чем вызов функции:

my_debug = True
...
if my_debug: logging.warn('disable me by setting my_debug = False')

поэтому, если мое понимание отладки верное, это похоже на отличный способ избавиться от ненужных вызовов регистрации. Flipside заключается в том, что он также отключает все ваши утверждения, поэтому это проблема, если вам нужны утверждения.

Ответ 3

Используйте pypreprocessor

Который также можно найти на PYPI (индекс пакета Python) и получить с помощью pip.

Вот пример базового использования:

from pypreprocessor import pypreprocessor

pypreprocessor.parse()

#define nologging

#ifdef nologging
...logging code you'd usually comment out manually...
#endif

По сути, препроцессор комментирует код так, как вы делали это вручную раньше. Он просто делает это "на лету" условно в зависимости от того, что вы определяете.

Вы также можете удалить все директивы препроцессора и прокомментировать код из постпроцессированного кода, добавив "pypreprocessor.removeMeta = True" между импортом и parse().

Файл вывода байт-кода (.pyc) будет содержать оптимизированный вывод.

SideNote: pypreprocessor совместим с python2x и python3k.

Отказ от ответственности: я являюсь автором pypreprocessor.

Ответ 4

Как несовершенный ярлык, как насчет издевательства logging в определенных модулях, используя что-то вроде MiniMock?

Например, если my_module.py был:

import logging
class C(object):
    def __init__(self, *args, **kw):
        logging.info("Instantiating")

Вы заменили бы ваше использование my_module на:

from minimock import Mock
import my_module
my_module.logging = Mock('logging')
c = my_module.C()

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

Получение специфического для уровня поведения было бы достаточно простым, насмехаясь над определенными методами или имея logging.getLogger возвращать макет-объект с некоторыми методами impotent и другими, делегирующими реальный модуль logging.

На практике вы, вероятно, захотите заменить MiniMock чем-то более простым и быстрым; по крайней мере, что-то, что не печатает использование в stdout! Конечно, это не устраняет проблему с модулем A, импортирующим logging из модуля B (и, следовательно, A также импортирует гранулярность журнала B)...

Это никогда не будет так быстро, как вообще не запускать операторы журналов, но должно быть намного быстрее, чем идти в глубину модуля ведения журнала, только чтобы обнаружить эту запись, не следует регистрировать в конце концов.

Ответ 5

Вы можете попробовать что-то вроде этого:

# Create something that accepts anything
class Fake(object):
    def __getattr__(self, key):
        return self
    def __call__(self, *args, **kwargs):
        return True

# Replace the logging module
import sys
sys.modules["logging"] = Fake()

Он по существу заменяет (или изначально заполняет) пространство для модуля протоколирования экземпляром Fake, который просто берет что угодно. Вы должны запустить вышеуказанный код (только один раз!), Прежде чем модуль регистрации будет использоваться где угодно. Вот тест:

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)-8s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename='/temp/myapp.log',
                    filemode='w')
logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bows')

С учетом выше ничего не регистрировалось, как и следовало ожидать.

Ответ 6

Я бы использовал какой-то причудливый декоратор оформления или их группу:

def doLogging(logTreshold):
    def logFunction(aFunc):
        def innerFunc(*args, **kwargs):
            if LOGLEVEL >= logTreshold:
                print ">>Called %s at %s"%(aFunc.__name__, time.strftime("%H:%M:%S"))
                print ">>Parameters: ", args, kwargs if kwargs else "" 
            try:
                return aFunc(*args, **kwargs)
            finally:
                print ">>%s took %s"%(aFunc.__name__, time.strftime("%H:%M:%S"))
        return innerFunc
    return logFunction

Все, что вам нужно, - объявить константу LOGLEVEL в каждом модуле (или просто глобально и просто импортировать его во все модули), а затем вы можете использовать его следующим образом:

@doLogging(2.5)
def myPreciousFunction(one, two, three=4):
    print "I'm doing some fancy computations :-)"
    return

И если LOGLEVEL не менее 2,5, вы получите результат следующим образом:

>>Called myPreciousFunction at 18:49:13
>>Parameters:  (1, 2) 
I'm doing some fancy computations :-)
>>myPreciousFunction took 18:49:13

Как вы можете видеть, необходима некоторая работа для лучшей обработки kwargs, поэтому значения по умолчанию будут напечатаны, если они присутствуют, но этот другой вопрос.

Вероятно, вы должны использовать модуль logger вместо необработанных print, но я хотел бы сосредоточиться на идее декоратора и избежать слишком долгого кода.

В любом случае - с помощью такого декоратора вы получаете регистрацию на уровне функций, произвольно много уровней журнала, простоту применения новой функции и отключить ведение журнала, вам нужно только установить LOGLEVEL. И вы можете определить различные выходные потоки/файлы для каждой функции, если хотите. Вы можете написать doLogging как:

 def doLogging(logThreshold, outStream=sys.stdout):
      .....
      print >>outStream, ">>Called %s at %s" etc.

И использовать файлы журналов, определенные на основе каждой функции.

Ответ 7

Это тоже проблема в моем проекте - регистрация заканчивается в отчетах профилировщика довольно последовательно.

Я использовал модуль _ast раньше в вилке PyFlakes (http://github.com/kevinw/pyflakes)... и это определенно возможно сделайте то, что вы предлагаете в своем вопросе, - чтобы инспектировать и вводить стражи перед вызовами методов ведения журнала (с подтвержденным предостережением, что вам нужно будет выполнить некоторую проверку типа времени выполнения). См. http://pyside.blogspot.com/2008/03/ast-compilation-from-python.html для простого примера.

Изменить: Я только что заметил MetaPython на моем планете planetpython.org - пример использования удаляет записи журнала во время импорта.

Возможно, лучшим решением было бы для кого-то перехватить регистрацию как модуль C, но я бы не стал первым прыгать на такую ​​возможность... p

Ответ 8

:-) Раньше мы называли препроцессором, и хотя препроцессор C имел некоторые из этих возможностей, "король холма" был препроцессором для мейнфрейма PL/I IBM. Он обеспечивал обширную языковую поддержку в препроцессоре (полные назначения, условные обозначения, цикл и т.д.), И было возможно написать "программы, которые писали программы", используя только PL/I PP.

Я написал много приложений с полномасштабной сложной трассировкой программ и данных (у нас не было приличного отладчика для внутреннего процесса в то время) для использования в разработке и тестировании, которые затем, когда скомпилированы с соответствующим "runtime flag" просто полностью удалил весь код трассировки без какого-либо влияния на производительность.

Я думаю, что идея декоратора хорошая. Вы можете написать декоратор, чтобы обернуть функции, требующие ведения журнала. Затем для распределения времени выполнения декоратор превращается в "no-op", который исключает операторы отладки.

Джон R

Ответ 9

В настоящее время я делаю проект, который использует обширную регистрацию для тестирования логики и времени выполнения для API анализа данных с использованием библиотеки Pandas.

Я нашел эту строку с аналогичной проблемой - например. каковы накладные расходы в операторах logging.debug, даже если уровень logging.basicConfig установлен на уровень = logging.WARNING

Я прибегал к написанию следующего script для комментирования или расторжения журнала отладки до развертывания:

import os
import fileinput

comment = True

# exclude files or directories matching string
fil_dir_exclude = ["__","_archive",".pyc"]

if comment :
    ## Variables to comment
    source_str = 'logging.debug'
    replace_str = '#logging.debug'
else :
    ## Variables to uncomment
    source_str = '#logging.debug'
    replace_str = 'logging.debug'

# walk through directories
for root, dirs, files in os.walk('root/directory') :
    # where files exist
    if files:
        # for each file
        for file_single in files :
            # build full file name
            file_name = os.path.join(root,file_single)
            # exclude files with matching string
            if not any(exclude_str in file_name for exclude_str in fil_dir_exclude) :
                # replace string in line
                for line in fileinput.input(file_name, inplace=True):
                    print "%s" % (line.replace(source_str, replace_str)),

Это рекурсия файла, которая исключает файлы на основе списка критериев и выполняет замену на основе найденного здесь ответа: Поиск и замена строки в файле на Python

Ответ 10

Мне нравится решение "if __debug_", за исключением того, что положить его перед каждым вызовом немного отвлекающим и уродливым. У меня была такая же проблема, и я преодолел ее, написав script, который автоматически анализирует ваши исходные файлы и заменяет записи протоколирования операторами pass (и закомментировал копии операторов ведения журнала). Он также может отменить это преобразование.

Я использую его, когда я развертываю новый код в производственной среде, когда есть множество операторов ведения журнала, которые мне не нужны в производственном настройке, и они влияют на производительность.

Здесь вы можете найти script: http://dound.com/2010/02/python-logging-performance/