LINUX.ORG.RU

Как мне в питоне включить отладочный лог для единственного модуля?

 ,


1

1

Извините, но я прочитал https://docs.python.org/3/library/logging.html и нихрена не понял.

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

Что они курили и что курить теперь мне?

UPD:

В качестве решения сделал модуль для манки-патчинга дефолтного логгера. См. в комментариях.

★★

Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от eternal_sorrow

а с чего ты взял, что это возможно?

Ну как минимум, можно сделать sed s/self.log.debug/self.log.info/.

А нахрена нужна библиотека логирования, которая даже такую простую задачу решить не в состоянии?

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

откуда logging узнает, из какого модуля идут логи?

Трассировка стека? С компилируемыми языками я такие трюки делал, не знаю можно ли такое в Питоне.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от eternal_sorrow

откуда logging узнает, из какого модуля идут логи?

Так у каждого модуля собственный экземпляр.

class Blabla(object):
    def __init__(self):
        self.log = logging.getLogger("Blabla")
wandrien ★★
() автор топика

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

Написать в начале модуля:

logging.getLogger("modname").setLevel(logging.ERROR)

Multiple calls to getLogger() with the same name will always return a reference to the same Logger object.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от X512

Написать в начале модуля:

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

wandrien ★★
() автор топика

В общем дописал в конструктор класса следующую простыню:

        console_logger = logging.StreamHandler()
        console_logger.setFormatter(logging.Formatter('[%(asctime)s] %(name)s %(message)s', "%H:%M:%S"))
        console_logger.setLevel(logging.DEBUG)
        self.log.addHandler(console_logger)
        self.log.setLevel(logging.DEBUG)

Это вместо простого self.log.setLevel(logging.DEBUG) в питоне так решаются проблемы.

Ну и еще один «незначительный» минус: всё, что ниже уровня DEBUG, теперь выводится в терминал дважды.

Еще хорошо, что у меня для каждого экземпляра класса создаётся уникальный логгер (передаю в logging.getLogger строку вида ИмяКласса.ИмяОбъекта), а то пришлось бы еще всё это завернуть в проверку, чтобы избежать многократной инициализации.

wandrien ★★
() автор топика

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

wandrien ★★
() автор топика

Тебя @rsync покусал? Еще много фейспалмов предстоит, привыкай раз уж вляпался в питон.

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

дописал в конструктор класса следующую простыню:

Еще не джабба, конечно, но уже серьезная заявка. Не знаю, на что. На рак мозга, вероятно.

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

Вообще-то обычно пишут в начале модуля

logger = logging.getLogger(__name__)

И вот ему ты так же можешь задать уровень логирования своими дефайнами или что ты там крутишь. Зачем в каждый класс свой пихать?

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

«Все идиоты, кроме меня»(с) Гомер Симпсон

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

Зачем в каждый класс свой пихать?

Java головного мозга?

X512 ★★★★★
()

Опять ему этот чёртов питон не угодил. Да что ж такое-то! Зачем ты на нём пишешь, если он тебе не нравится? Жалуешься прямо как безвольный раб.

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

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

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от t184256

ПЕРЕКОМПИЛИРУЕШЬ

Естественно, перекомпилируешь. Если я правлю текст модуля в ходе работы, мне его один хрен перекомпилировать.

А питон я в докер-образ пересобираю, например. Тоже не мгновенная готовность.

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

wandrien ★★
() автор топика

Накидал такой костыль, чтобы выборочно повышать сообщения с DEBUG до INFO.

import logging
import re

log_level_raising_rules = []

def addLogLevelRaisingRule(rule, level=None):
    if level is None:
        level = logging.INFO
    log_level_raising_rules.append({
        "rule": rule,
        "level": level
    })

def matchLogLevelRaisingRule(name):
    for rule in log_level_raising_rules:
        if isinstance(rule["rule"], re.Pattern):
            if rule["rule"].search(name):
                return rule["level"]
        else:
            if rule["rule"] == name:
                return rule["level"]
    return None

class SelectiveLogger(logging.getLoggerClass()):
    def __init__(self, name, level=logging.NOTSET):
        return super().__init__(name, level)

    def raiseLevel(self, level):
        raised_level = matchLogLevelRaisingRule(self.name)
        if raised_level is not None:
            if level < raised_level:
                level = raised_level
        return level

    def isEnabledFor(self, level):
        level = self.raiseLevel(level)
        return super().isEnabledFor(level)

    def _log(self, level, msg, args, **kwargs):
        level = self.raiseLevel(level)
        return super()._log(level, msg, args, **kwargs)

logging.setLoggerClass(SelectiveLogger)

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 2)
Ответ на: комментарий от anonymous

Тебя @rsync покусал? Еще много фейспалмов предстоит, привыкай раз уж вляпался в питон.

/rsync mode on

Тут вот в интернетах спрашивают: Why doesn’t list have a safe «get» method like dictionary?

Ответ: Probably because it just didn’t make much sense for list semantics.

Унифицированная работа с коллекциями? Не, не слышали.

Боже, да разработчики https://ruby-doc.org/core/Enumerable.html вообще тогда гении.

/rsync mode off

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

А контекст, для чего это нужно?

Чтобы не писать лесенку if-ов. Типа:

value = d.get(key1, {}).get(key2, {}).get(key3, None)
if value is None:
    # Value not set.
    return
wandrien ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.