Як відключити та знову ввімкнути консольний журнал у Python?


154

Я використовую модуль реєстрації Python, і я хочу відключити консольний журнал на деякий час, але він не працює.

#!/usr/bin/python
import logging

logger = logging.getLogger() # this gets the root logger
# ... here I add my own handlers 
#logger.removeHandler(sys.stdout)
#logger.removeHandler(sys.stderr)

print logger.handlers 
# this will print [<logging.StreamHandler instance at ...>]
# but I may have other handlers there that I want to keep

logger.debug("bla bla")

Вищевказаний код відображає bla blaвкладку stdout, і я не знаю, як можна безпечно відключити обробник консолі. Як я можу бути впевнений, що тимчасово видаляю консоль StreamHandler, а не іншу?


Для тих, хто цікавиться, чому хтось захоче відключити ведення журналу: Ви не хочете реєструвати приватні дані, наприклад паролі чи ключі API.
Stevoisiak

4
@StevenVascellaro. Чому саме ті, кого відправляють до лісоруба в першу чергу? Це не звучить правильно ...
Божевільний фізик

1
@MadPhysicist У мене є програма, яка надсилає XML-запити до зовнішнього API. За замовчуванням ці запити реєструються у файлі. Однак для початкового входу потрібна автентифікація з іменем користувача та паролем, які я не хочу ввійти.
Stevoisiak

@StevenVascellaro. Я бачу. Дякую за пояснення.
Божевільний фізик

Ви не показуєте, як / де ви додаєте ваші обробники. Якби вони були додані в кореневій реєстраторі це запобіжить реєстрацію від додавання по замовчуванням StreamHandler , як описано в docs.python.org/3/library/logging.html#logging.basicConfig Крім того , в пов'язане опис по замовчуванням StreamHandler додаються тільки в протягом першого Повідомлення журналу виклику виклику, тому під час друку logger.handlersвоно повинно бути порожнім (як це передує logger.debug()дзвінку). Код, про який йдеться, відображається лише [](порожній список обробників). Перевірено за допомогою Python 2.7.15 та Python 3.6.6.
Пьотр Доброгост

Відповіді:


197

Я знайшов рішення для цього:

logger = logging.getLogger('my-logger')
logger.propagate = False
# now if you use logger it will not log to console.

Це запобіжить надсилання журналу у верхній реєстратор, який включає консольний журнал.


8
Я не думаю, що це хороше рішення. Не розповсюдження на більш високі лісоруби може мати інші небажані наслідки.
lfk

2
Якщо ви хочете лише фільтрувати повідомлення нижче певного рівня журналу (скажімо, всі INFOповідомлення), ви можете змінити другий рядок на щось на кшталтlogger.setLevel(logging.WARNING)
Hartley Brody

2
Як би ви знову ввімкнули журнал після цього?
Stevoisiak

4
Чи не відповідь , як блокування поширення ефективно відключає всі обробники кореневого реєстратора , і питання чітко сказано (...) , але я , можливо, інші обробники там , що я хочу , щоб що свідчить про намір, щоб відключити по замовчуванням StreamHandler кореневого реєстратора тільки .
Пьотр Доброгост

Припинення поширення повідомлень недостатньо. Починаючи з Python 3.2 , logging.lastResortобробник все ще буде реєструвати повідомлення суворості logging.WARNINGта більшості sys.stderrза відсутності інших обробників. Дивіться мою відповідь .
Маджієро

106

Я використовую:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False

9
це також працює на рівні loggingмодуля, щоб повністю відключити ведення журналу , наприклад import logging; logging.disable(logging.CRITICAL);:: docs.python.org/2/library/logging.html#logging.disable
lsh

1
Це набагато краще, ніж відключення розповсюдження.
Mátray Márk

6
Не відповідь - питання задає питання, як відключити лише стандартний StreamHandler .
Пьотр Доброгост

1
disabledАтрибут не є частиною громадського API. Див. Bugs.python.org/issue36318 .
Maggyero

69

Ви можете використовувати:

logging.basicConfig(level=your_level)

де ваш_рівень є одним із таких:

      'debug': logging.DEBUG,
      'info': logging.INFO,
      'warning': logging.WARNING,
      'error': logging.ERROR,
      'critical': logging.CRITICAL

Отже, якщо ви встановите свій_уровень для ведення журналу.CRITICAL , ви отримаєте лише критичні повідомлення, надіслані:

logging.critical('This is a critical error message')

Встановлення вашого_рівню на реєстрацію.DEBUG покаже всі рівні ведення журналу.

Щоб отримати детальнішу інформацію, перегляньте приклади реєстрації.

Таким же чином, щоб змінити рівень для кожного обробника, використовуйте функцію Handler.setLevel () .

import logging
import logging.handlers

LOG_FILENAME = '/tmp/logging_rotatingfile_example.out'

# Set up a specific logger with our desired output level
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)

# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(
          LOG_FILENAME, maxBytes=20, backupCount=5)

handler.setLevel(logging.CRITICAL)

my_logger.addHandler(handler)

6
Це, як правило, корисна інформація, але в питанні було задано питання про те, як відключити консольний журнал, а не як додати додатковий обробник. якби ви вивчали my_logger.handlers з наведеним вище кодом, застосованим до оригінального прикладу, ви побачили б два обробника - ваш новий обробник файлів та оригінальний обробник потоку.
Джо

КРИТИЧНЕ було слово, яке я шукав. Дякую.
Nishant

Я хотів би бачити рівень виправлення OFF. Це однозначно і просто.
Не машина

46

(давнє мертве запитання, але для майбутніх шукачів)

Ближче до оригінального коду / наміру плаката, це працює для мене під python 2.6

#!/usr/bin/python
import logging

logger = logging.getLogger() # this gets the root logger

lhStdout = logger.handlers[0]  # stdout is the only handler initially

# ... here I add my own handlers 
f = open("/tmp/debug","w")          # example handler
lh = logging.StreamHandler(f)
logger.addHandler(lh)

logger.removeHandler(lhStdout)

logger.debug("bla bla")

Треба було відпрацювати, щоб видалити обробник stdout після додавання нового; з'являється код реєстратора, який автоматично повторно додає stdout, якщо немає обробників.


2
Послідовність logger = logging.getLogger(); lhStdout = logger.handlers[0]помилкова, оскільки кореневий реєстратор спочатку не має обробників - python -c "import logging; assert not logging.getLogger().handlers". Перевірено за допомогою Python 2.7.15 та Python 3.6.6.
Пьотр Доброгост

42

Контекстний менеджер

import logging 
class DisableLogger():
    def __enter__(self):
       logging.disable(logging.CRITICAL)
    def __exit__(self, a, b, c):
       logging.disable(logging.NOTSET)

Приклад використання:

with DisableLogger():
    do_something()

Мені дуже подобається ця ідіома, але я скоріше зможу відключити певний простір імен. Наприклад, я просто хочу, щоб кореневий реєстратор тимчасово вимкнувся. Хоча, використовуючи цю ідіому, ми повинні мати можливість лише тимчасово додати / видалити обробники тощо.
Кріс

1
Питання задає питання, як відключити лише стандартний StreamHandler .
Пьотр Доброгост

1
Вам не потрібно прокручувати свій власний клас, ви можете використовувати @contextmanager з контекстувальної програми та записати поступальну функцію
KristianR

Якщо ви їдете до екзотичних фруктів на своїй піці. Звичайно.
user3504575

34

Щоб повністю відключити ведення журналу :

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

Щоб увімкнути ведення журналу :

logging.disable(logging.NOTSET)

Інші відповіді пропонують робочі кола, які не повністю вирішують проблему, наприклад

logging.getLogger().disabled = True

а для деяких nбільше 50

logging.disable(n)

Проблема першого рішення полягає в тому, що він працює лише для кореневого реєстратора. Інші реєстратори, створені за допомогою, скажімо, logging.getLogger(__name__)цим методом не відключені.

Друге рішення впливає на всі журнали. Але це обмежує вихід на рівні вище заданого, так що можна змінити його за допомогою реєстрації з рівнем, що перевищує 50.

Цьому можна запобігти

logging.disable(sys.maxint)

що, наскільки я можу сказати (після перегляду джерела ), це єдиний спосіб повністю відключити ведення журналу.


1
Downvote як питання задає питання, як відключити лише
Piotr

27

Тут є кілька справді приємних відповідей, але, мабуть, найпростіший не береться занадто багато до уваги (тільки з infinito).

root_logger = logging.getLogger()
root_logger.disabled = True

Це відключає кореневий реєстратор, а отже, і всі інші реєстратори. Я насправді не перевіряв, але це також має бути найшвидшим.

З коду реєстрації в python 2.7 я бачу це

def handle(self, record):
    """
    Call the handlers for the specified record.

    This method is used for unpickled records received from a socket, as
    well as those created locally. Logger-level filtering is applied.
    """
    if (not self.disabled) and self.filter(record):
        self.callHandlers(record)

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


1
Якщо я не роблю щось не так, це лише вимикає кореневий реєстратор, а не будь-який створений на зразокlog = logging.getLogger(__name__)
starfry

2
Це може бути проблематично, якщо ви маєте справу з декількома реєстраторами чи кількома обробниками. Наприклад, якщо ви все ще хочете увійти до файлу, але хочете відключити обробник потоку в конкретному випадку.
Джо

1
Це вимикає кореневий реєстратор, а отже, і всі інші реєстратори - строго кажучи, відключення кореневого реєстратора не відключає жодних інших реєстраторів. Крім того, питання задає питання про вимкнення лише StreamHandler за замовчуванням .
Пьотр Доброгост

disabledАтрибут не є частиною громадського API. Див. Bugs.python.org/issue36318 .
Маджієро

10

Не потрібно відволікати stdout. Ось кращий спосіб зробити це:

import logging
class MyLogHandler(logging.Handler):
    def emit(self, record):
        pass

logging.getLogger().addHandler(MyLogHandler())

Ще простішим способом є:

logging.getLogger().setLevel(100)

4
У Python 2.7+ це доступно як NullHandler ()
П'єр

1
Причину, по якій це працює (вимикає за замовчуванням StreamHandler), можна побачити, читаючи опис logging.basicConfig()функції (міна акценту): чи основна конфігурація для системи журналу, створивши StreamHandler за допомогою замовчувача Formatter та додавши його до кореневого журналу. Функції налагодження (), інформація (), попередження (), помилка () та критичний () автоматично викликають basicConfig (), якщо для кореневого реєстратора не визначено обробників . - docs.python.org/3/library/logging.html#logging.basicConfig
Piotr

2

Я не дуже добре знаю модуль реєстрації, але використовую його таким чином, що зазвичай хочу відключити лише налагоджувальні (або інформаційні) повідомлення. Можна використовуватиHandler.setLevel() для встановлення рівня журналу значення CRITICAL або вище.

Крім того, ви можете замінити sys.stderr і sys.stdout файлом, відкритим для запису. Див. Http://docs.python.org/library/sys.html#sys. stdout . Але я б не рекомендував цього.


Це може працювати, якщо logger.handlers містив би щось, що є [].
sorin

2

Ви також можете:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers

Чому ви користуєтесь тим, app.loggerщо навіть не вказуєте замість кореневого реєстратора, прямо вказаного у питанні ( logging.getLogger()) та більшості відповідей? Як ви знаєте, що можете безпечно змінювати handlersвластивість замість виклику Logger.addHandlerметоду?
Пьотр Доброгост

2
import logging

log_file = 'test.log'
info_format = '%(asctime)s - %(levelname)s - %(message)s'
logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'info_format': {
            'format': info_format
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'info_format'
        },
        'info_log_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'INFO',
            'filename': log_file,
            'formatter': 'info_format'
        }
    },
    'loggers': {
        '': {
            'handlers': [
                'console',
                'info_log_file'
            ],
            'level': 'INFO'
        }
    }
})


class A:

    def __init__(self):
        logging.info('object created of class A')

        self.logger = logging.getLogger()
        self.console_handler = None

    def say(self, word):
        logging.info('A object says: {}'.format(word))

    def disable_console_log(self):
        if self.console_handler is not None:
            # Console log has already been disabled
            return

        for handler in self.logger.handlers:
            if type(handler) is logging.StreamHandler:
                self.console_handler = handler
                self.logger.removeHandler(handler)

    def enable_console_log(self):
        if self.console_handler is None:
            # Console log has already been enabled
            return

        self.logger.addHandler(self.console_handler)
        self.console_handler = None


if __name__ == '__main__':
    a = A()
    a.say('111')
    a.disable_console_log()
    a.say('222')
    a.enable_console_log()
    a.say('333')

Вихід консолі:

2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,358 - INFO - A object says: 333

Вміст файлу test.log:

2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,357 - INFO - A object says: 222
2018-09-15 15:22:23,358 - INFO - A object says: 333

2
Додайте опис коду. Це допомогло б набагато краще
Mathews Sunny

2

Змінивши один рівень у "logging.config.dictConfig", ви зможете підняти весь рівень реєстрації на новий рівень.

logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
    'console': {
        'format': '%(name)-12s %(levelname)-8s %(message)s'
    },
    'file': {
        'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'
    }
},
'handlers': {
    'console': {
        'class': 'logging.StreamHandler',
        'formatter': 'console'
    },
#CHANGE below level from DEBUG to THE_LEVEL_YOU_WANT_TO_SWITCH_FOR
#if we jump from DEBUG to INFO
# we won't be able to see the DEBUG logs in our logging.log file
    'file': {
        'level': 'DEBUG',
        'class': 'logging.FileHandler',
        'formatter': 'file',
        'filename': 'logging.log'
    },
},
'loggers': {
    '': {
        'level': 'DEBUG',
        'handlers': ['console', 'file'],
        'propagate': False,
    },
}

})


1

Знайшли елегантне рішення за допомогою декораторів , яке вирішує наступну проблему: що робити, якщо ви пишете модуль з декількома функціями, кожна з яких має кілька повідомлень про налагодження, і ви хочете відключити вхід у журнал усіх функцій, крім тієї, на яку ви зараз зосереджені?

Зробити це можна за допомогою декораторів:

import logging, sys
logger = logging.getLogger()
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)


def disable_debug_messages(func):
    def wrapper(*args, **kwargs):
        prev_state = logger.disabled
        logger.disabled = True
        result = func(*args, **kwargs)
        logger.disabled = prev_state
        return result
    return wrapper

Потім ви можете:

@disable_debug_messages
def function_already_debugged():
    ...
    logger.debug("This message won't be showed because of the decorator")
    ...

def function_being_focused():
    ...
    logger.debug("This message will be showed")
    ...

Навіть якщо ви телефонуєте function_already_debuggedзсередини function_being_focused, налагоджуйте повідомлення зfunction_already_debugged не з’являться. Це гарантує, що ви побачите лише повідомлення про налагодження з функції, на яку фокусуєтесь.

Сподіваюся, це допомагає!


0

Якщо ви хочете тимчасово відключити певний реєстратор, ось що робиться.

Приклад журналу

2019-10-02 21:28:45,663 django.request PID: 8  Internal Server Error: /service_portal/get_all_sites

Код

django_request_logger = logging.getLogger('django.request')
django_request_logger.disabled = True
django_request_logger.disabled = False

0

У бібліотеці Logging Python можна повністю відключити ведення журналу (для всіх рівнів) для певного реєстратора, виконавши одну з наступних дій:

  1. Додавання в журнал logging.NullHandler()обробника (щоб запобігти logging.lastResortобробці журналів подій важкості logging.WARNINGі більше до sys.stderr) та встановити propagateатрибут цього реєстратораFalse (щоб запобігти передачі журналу подій обробникам його реєстраторів-предків).

    • Використання основного API:

      import logging
      
      logging.getLogger("foo").addHandler(logging.NullHandler())
      logging.getLogger("foo").propagate = False
    • Використання API config:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "handlers": {
              "null": {
                  "class": "logging.NullHandler"
              }
          },
          "loggers": {
              "foo": {
                  "handlers": ["null"],
                  "propagate": False
              }
          }
      })
  2. Додавання до реєстратора lambda record: Falseфільтра.

    • Використання основного API:

      import logging
      
      logging.getLogger("foo").addFilter(lambda record: False)
    • Використання API config:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "filters": {
              "all": {
                  "()": lambda: (lambda record: False)
              }
          },
          "loggers": {
              "foo": {
                  "filters": ["all"]
              }
          }
      })

Увага. - На відміну від 1-го рішення, 2-е рішення не вимикає ведення журналу з дочірніх реєстраторів (наприклад logging.getLogger("foo.bar")), тому його слід використовувати лише для відключення журналу для одного реєстратора.

Примітка. - Встановлення disabledатрибута реєстратора Trueне є третім рішенням, оскільки воно не є частиною загальнодоступного API. Дивіться https://bugs.python.org/issue36318 :

import logging

logging.getLogger("foo").disabled = True  # DO NOT DO THAT

-1

підклас обробника, який потрібно тимчасово відключити:

class ToggledHandler(logging.StreamHandler):
"""A handler one can turn on and off"""

def __init__(self, args, kwargs):
    super(ToggledHandler, self).__init__(*args, **kwargs)
    self.enabled = True  # enabled by default

def enable(self):
    """enables"""
    self.enabled = True

def disable(self):
    """disables"""
    self.enabled = False

def emit(self, record):
    """emits, if enabled"""
    if self.enabled:
        # this is taken from the super's emit, implement your own
        try:
            msg = self.format(record)
            stream = self.stream
            stream.write(msg)
            stream.write(self.terminator)
            self.flush()
        except Exception:
            self.handleError(record)

знайти обробника по імені досить просто:

_handler = [x for x in logging.getLogger('').handlers if x.name == your_handler_name]
if len(_handler) == 1:
    _handler = _handler[0]
else:
    raise Exception('Expected one handler but found {}'.format(len(_handler))

одного разу знайдено:

_handler.disable()
doStuff()
_handler.enable()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.