LINUX.ORG.RU

python debugging output


0

2

Товарищи! Мне нужно в коде на Питоне в стратегических местах расставить отладочные print'ы, однако, которые можно было бы выключить простой установкой глобальной переменной ну или еще каким удобным образом + бонусом, чтобы у принтов на разные значения вроде ошибки или успешного выполнения были бы разные префиксы.

Нашел logger, пока написал вот чего, но уже очень оно большое и несуразное, нет ли способа попроще?

class CritFilter(logging.Filter):
    def filter(self, rec):
        return rec.levelno == logging.CRITICAL

class WarnFilter(logging.Filter):
    def filter(self, rec):
        return rec.levelno == logging.WARNING

class InfoFilter(logging.Filter):
    def filter(self, rec):
        return rec.levelno == logging.INFO

class DebugFilter(logging.Filter):
    def filter(self, rec):
        return rec.levelno == logging.DEBUG

log = logging.getLogger()   # root
log.setLevel(logging.DEBUG)

hdlr_crit = logging.StreamHandler()
hdlr_crit.setLevel(logging.CRITICAL)
hdlr_crit.setFormatter(logging.Formatter("*** ERROR: %(message)s"))
hdlr_crit.addFilter(CritFilter())
log.addHandler(hdlr_crit)

hdlr_warn = logging.StreamHandler()
hdlr_warn.setLevel(logging.WARNING)
hdlr_warn.setFormatter(logging.Formatter(">!* %(message)s"))
hdlr_warn.addFilter(WarnFilter())
log.addHandler(hdlr_warn)

hdlr_info = logging.StreamHandler()
hdlr_info.setLevel(logging.INFO)
hdlr_info.setFormatter(logging.Formatter("> + %(message)s"))
hdlr_info.addFilter(InfoFilter())
log.addHandler(hdlr_info)

hdlr_debug = logging.StreamHandler()
hdlr_debug.setLevel(logging.DEBUG)
hdlr_debug.setFormatter(logging.Formatter("> %(message)s"))
hdlr_debug.addFilter(DebugFilter())
log.addHandler(hdlr_debug)

log.critical("We are under attack!")
log.warning("More wood is needed.")
log.info("Unable to comply.")
log.debug("Unit ready.")

# yields:
#*** ERROR: We are under attack!
#>!* More wood is needed.
#> + Unable to comply.
#> Unit ready.

однако, которые можно было бы выключить простой установкой глобальной переменной ну или еще каким удобным образом

нет ли способа попроще?

logger.setLevel куда уж проще.

baverman ★★★
()

Пропустил.

+ бонусом, чтобы у принтов на разные значения вроде ошибки или успешного выполнения были бы разные префиксы.

Сделай свой _один_ Formatter.

baverman ★★★
()
Ответ на: комментарий от baverman

Зачем _один_ форматтер, ежели мне требуется разные форматтеры для разных северити?

xio
() автор топика
Ответ на: комментарий от xio

Зачем _один_ форматтер, ежели мне требуется разные форматтеры для разных северити?

Ok. Я, наверно, не совсем понятно выразился.

Сделай композитный форматтер, диспатчащий вызов format(record) нужному, в зависимости от северити.

В общем, форматирование вывода это задача не фильтров, а форматтера.

baverman ★★★
()
Ответ на: комментарий от baverman

Предлагаешь заменить 4 хэндлера одним и форматировать его вывод? Ок, как тогда фильтровать только нужные северити, а не все, выше определенного (поведение по умолчанию)?

xio
() автор топика
Ответ на: комментарий от xio

Ок, как тогда фильтровать только нужные северити, а не все, выше определенного

Соответственно, свой фильтр. Насколько я знаю, из коробки такого фильтра нет.

baverman ★★★
()
Ответ на: комментарий от baverman

Эх, ну ладно.

А вообще-то чем по факту большинство пользуется, не в курсе? Просто переписывание классов немного не интуитивно и не всегда соразмерно задаче по трудозатратам.

xio
() автор топика
Ответ на: комментарий от xio

А вообще-то чем по факту большинство пользуется, не в курсе?

Ну вообще-то logging это стандарт логирования. Есть правда стихийные велосипеды. Но это все максимализм.

Просто переписывание классов немного не интуитивно и не всегда соразмерно задаче по трудозатратам

Нестандартным задачам — нестандартные решения. Ты же осознаешь, что требования у тебя как минимум странные?

Хотя в твоем случае — это 20-30 строчек. Думаю, не так уж и много.

baverman ★★★
()

Не, оно грамотное :).

Варианта два:

1) Делать логгирование включаемым/отключаемым для класса/объекта:

#!/usr/bin/env python2
from __future__ import print_function

import logging
logger = logging.getLogger()



class Log(object):
    _logging_ = True

    #def __init__(self):
    #    self._logging_ = True


    def __getattr__(self, lvlname):
        #this will rise AttributeError if we haven't got this loglevel
        loggerfunc = getattr(logger, lvlname)

        if not self._logging_:
            return self._dummy_

        def func(self, msg):
            loggerfunc("{0}: %s".format(self.__repr__()), msg)

        setattr(self.__class__, lvlname, func)

        return getattr(self, lvlname)


    def _dummy_(self, *args):
        print("dummy log :)")
        pass


    def __repr__(self):
        #raise Exception("Override this method in child")
        kname = self.__class__.__name__
        return "{klass}(id={id})".format(klass=kname, id=hex(id(self)))




if __name__ == "__main__":
    #logger setup
    con_handler = logging.StreamHandler()
    con_handler.setLevel(logging.DEBUG)
    con_fmt = logging.Formatter("LOG: %(name)s - %(levelname)s: %(message)s")
    con_handler.setFormatter(con_fmt)
    logger.addHandler(con_handler)
    logger.setLevel(logging.DEBUG)

    class A(Log):
        def __repr__(self):
            return "<Class A>"
    a1 = A()
    a1.debug("A debug1")
    a1.debug("A debug2")
    a2 = A()
    a2.debug("A debug")
    class B(Log):
        def __repr__(self):
            return "<Class B>"
    b = B()
    b.debug("B debug")

2) делать что-то типа debug(SUBSYSTEM, «your msg») и делать фильтрацию по параметру SUBSYSTEM. Т.е. if SUBSYSTEM: print msg; else return

3) можно сочетать оба варианта

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Очень странный подход к логированию. Очень, очень. Почему нельзя использовать иерархию неймспейсов для точечного управления?

baverman ★★★
()
Ответ на: комментарий от baverman

Почему нельзя использовать иерархию неймспейсов

нужно! просто и времени не было и этот код полностью решает задачу. А так можно такие вещи улучшать до бесконечности.

PS а в чём странность подхода?

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

PS а в чём странность подхода?

* Логгер, это логгер, не надо его задачи делегировать в каждый объект. Чем не устраивает logger.error('%s: Error msg', self) или даже logger.error('Error msg', extra={'obj':self}) + кастомный форматтер, который красивенько выведет контекст. Решительно непонятно.

* Настройка шумности логов (да вообще всей системы логирования) — это внешняя по-отношению к коду штука. Ты же знаешь, что logging может быть сконфигурирован посредством dict или текстового файла, а они абсолютно ничего не знают о атрибуте _logging_, и воздействовать придется через код.

2) делать что-то типа debug(SUBSYSTEM, «your msg»)

* Это покрывается возможностью организовать иерархию логгеров, нечего городить костыли.

baverman ★★★
()
Ответ на: комментарий от baverman

> * Логгер, это логгер, не надо его задачи делегировать в каждый объект

Если ты вписал в объект logger.error() ты уже его встроил. Вместо того чтобы каждый раз указывать кастомный форматер проще его вот таким вот образом встроить в класс. Это не костыль, это наоборот избавление от избыточного кода.


logging может быть сконфигурирован посредством dict


Гибкости этого модуля недостаточно, я его рассматриваю только как базу для создания своих логгерах


ничего не знают о атрибуте _logging_, и воздействовать придется через код.


зато мой логгер об этом знает, см. код что я привёл.


2) делать что-то типа debug(SUBSYSTEM, «your msg»)

* Это покрывается возможностью организовать иерархию логгеров, нечего городить костыли.


Как ты предлагаешь указать место в иерархии?

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

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

Обычно избавляются от избыточных связей, а не от лишнего кода. Наследование от Log, как раз такая лишняя связь. Запихивание в __repr__ форматирование объекта для логов — тоже лишняя связь.

Гибкости этого модуля недостаточно, я его рассматриваю только как базу для создания своих логгерах

Это был просто пример, основная мысль — конфигурирование логирования опирается и будет опираться на api модуля logging, поэтому самопальные рычаги управления плохи и вредны.

зато мой логгер об этом знает, см. код что я привёл.

А разработчик, пришедший после тебя не знает и это очень сильно усложнит ему жизнь.

Как ты предлагаешь указать место в иерархии?

logging.get_logger('system.subsystem')?

baverman ★★★
()
Ответ на: комментарий от baverman

> Обычно избавляются от избыточных связей, а не от лишнего кода. Наследование от Log, как раз такая лишняя связь. Запихивание в __repr__ форматирование объекта для логов — тоже лишняя связь.

не соглашусь с этим. Логи не только для отладки используются, в том или ином виде они у меня всегда для сервиса есть. На __repr__ ты зря гонишь, он-то и отвечает за представление объекта в виде строки. И, естественно, это поле должно быть переопределено.

И вообще, по твоей логике весь ооп хрень полная потому что он плодит связи.


А разработчик, пришедший после тебя не знает и это очень сильно усложнит ему жизнь.


не драматизируй, там всё предельно ясно.


Как ты предлагаешь указать место в иерархии?


logging.get_logger('system.subsystem')?


нормальный метод, спасибо, подумаю над этим.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

И вообще, по твоей логике весь ооп хрень полная потому что он плодит связи.

Оверинженеринг плох, ооп ради ооп не нужен. Да, в целом, ты правильно меня понял.

На __repr__ ты зря гонишь, он-то и отвечает за представление объекта в виде строки.

Я имел ввиду, что «представление объекта в виде строки» не всегда совпадает с тем, что должно быть в логах. Например, иногда полезно соблюдение инварианта obj == eval(repr(obj)), а в логе такой крокодил будет страшен.

baverman ★★★
()
Ответ на: комментарий от true_admin

> _logging_

_dummy_

PEP8

self.__repr__()

repr(self)

setattr(self.__class__, lvlname, func)

Пожалуйста, никогда не делайте так в коде, который будет поддерживать кто-то еще.

#raise Exception(«Override this method in child»)

raise NotImplementedError, а еще лучше abc.ABCMeta

Рекомендуемое поведение __repr__() описано в документации, но про это уже сказали выше.

ntp
()
Ответ на: комментарий от ntp

не делайте так в коде, который будет поддерживать кто-то еще.

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

pep8 хорошо знаю, у меня просто есть к нему свои дополнения. Ты читал его? Там в самом начале «sometimes the style guide just doesn't apply», это помимо того что GvR сказал «rules are to be broken».

repr(self)

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

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

> каким образом предлагаешь делать lazy-атрибуты?

Зачем вам в простейшей задаче lazy-атрибуты и модифицируещиеся в рантайме классы? Я еще раз повторю: Пожалуйста, никогда не делайте так в коде, который будет поддерживать кто-то еще.

И, мне кажется, ты не понимаешь что это делает.

Чего тут непонятного? Юный разработчик узнал интересную возможность, теперь будет использовать в любой подходящий и неподходящий момент. Про метаклассы уже слышали?

Там в самом начале «sometimes the style guide just doesn't apply»

Я знаю только один случай когда не стоит придерживаться PEP8: когда работаешь с legacy-кодом, в котором почему-то свой собственный стиль.

у меня просто есть к нему свои дополнения

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

ntp
()
Ответ на: комментарий от ntp

в простейшей задаче lazy-атрибуты и модифицируещиеся в рантайме классы?

Ужас-ужас, а что с тобой будет когда сырцы django увидишь...

Юный разработчик узнал интересную возможность

Нечего сказать по делу - лучше не пиши.

Лучше используйте общепринятый стандарт, даже если он вас не устраивает.

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

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

> Ужас-ужас, а что с тобой будет когда сырцы django увидишь.

Когда это нагромождение велосипедов и костылей стало примером для подражания? Кстати, покажите, пожалуйста, где в основных классах джанго используется переопределение __getattr__. Помню когда шел рефакторинг работы с файлами даже разработчики джанги в итоге пришли к django.core.files.utils.FileProxyMixin. Взгляните на него, кстати.

почему именно так задумано

Ok. Почему?

человек сразу отметает то к чему не привык.

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

ntp
()
Ответ на: комментарий от ntp

Ok. Почему?

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

true_admin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.