Как отключить и снова включить консольное ведение журнала в 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 blaon-stdout, и я не знаю, как можно безопасно отключить обработчик консоли. Как я могу быть уверен, что я временно удаляю консоль StreamHandler, а не другую?

Сорин
источник
Для тех, кто интересуется, почему кто-то захочет отключить ведение журнала: вам не нужно регистрировать личные данные, такие как пароли или ключи API.
Stevoisiak
4
@StevenVascellaro. Почему их отправляют в логгер? Это звучит неправильно ...
Безумный физик
1
@MadPhysicist У меня есть приложение, которое отправляет запросы XML на внешний API. По умолчанию эти запросы записываются в файл. Тем не менее, первоначальный вход требует аутентификации с использованием имени пользователя и пароля, которые я не хочу регистрировать.
Стевойсяк
@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
Я не думаю, что это хорошее решение. Непредставление более высоким регистраторам может иметь другие нежелательные последствия.
17
2
Если вы хотите фильтровать сообщения только ниже определенного уровня журнала (скажем, все INFOсообщения), вы можете изменить вторую строку на что-то вродеlogger.setLevel(logging.WARNING)
Хартли Броуди
2
Как бы вы снова включили журнал?
Stevoisiak
4
Не ответ , как блокирование распространения эффективно отключает все обработчики корневого регистратора , и вопрос четко сказано (...) , но я , возможно, другие обработчики там , что я хочу , чтобы что свидетельствует о намерении, чтобы отключить по умолчанию StreamHandler корневого регистратора только .
Петр Доброгост
Остановить распространение сообщений недостаточно. Начиная с Python 3.2 , logging.lastResortобработчик по-прежнему будет регистрировать сообщения с серьезностью logging.WARNINGи выше sys.stderrв отсутствие других обработчиков. Смотри мой ответ .
Maggyero
106

Я использую:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
Infinito
источник
9
это также работает на loggingуровне модуля, чтобы полностью отключить ведение журнала , например import logging; logging.disable(logging.CRITICAL);:: docs.python.org/2/library/logging.html#logging.disable
lsh
1
Это намного лучше, чем отключение распространения.
Матрай Марк,
6
Не ответ - вопрос состоит в том, как отключить только StreamHandler по умолчанию .
Петр Доброгост
1
disabledАтрибут не является частью общественного API. См. Bugs.python.org/issue36318 .
Маджеро
69

Ты можешь использовать:

logging.basicConfig(level=your_level)

где ваш_уровень является одним из таких:

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

Итак, если вы установите your_level в logging.CRITICAL , вы получите только критические сообщения , отправленные:

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

Установка your_level для ведения журнала. 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)
Vadikus
источник
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, если нет обработчиков.

l82ky
источник
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()
pymen
источник
Мне действительно нравится эта идиома, но я бы предпочел отключить определенное пространство имен. Например, я просто хочу, чтобы корневой логгер был временно отключен. Хотя, используя эту идиому, мы должны иметь возможность просто временно добавлять / удалять обработчики и тому подобное.
Крис
1
Вопрос в том, как отключить только StreamHandler по умолчанию .
Петр Доброгост
1
Вам не нужно бросать свой собственный класс, вы можете использовать @contextmanager из contextlib и написать функцию выдачи
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)

что, насколько я могу судить (после просмотра источника ), является единственным способом полностью отключить ведение журнала.

starfry
источник
1
Даунвот как вопрос спрашивает как отключить только
Петр Доброгост
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)

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

andrea_crotti
источник
1
Если я не делаю что-то не так, это отключает только корневой логгер, а не созданный какlog = logging.getLogger(__name__)
starfry
2
Это может быть проблематично, если вы имеете дело с несколькими регистраторами или несколькими обработчиками. Если, например, вы все еще хотите войти в файл, но хотите отключить потоковый обработчик в конкретном случае.
Джо
1
Это отключает корневой регистратор и, следовательно, все остальные регистраторы - строго говоря, отключение корневого регистратора не отключает никаких других регистраторов. Кроме того, вопрос касается отключения только по умолчанию StreamHandler .
Петр Доброгост
disabledАтрибут не является частью общественного API. См. Bugs.python.org/issue36318 .
Maggyero
10

Не нужно отвлекать стандартный вывод. Вот лучший способ сделать это:

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 по умолчанию и добавив его в корневой регистратор. Функции debug (), info (), warning (), error () и crit () будут автоматически вызывать basicConfig (), если для корневого регистратора не определены обработчики . - docs.python.org/3/library/logging.html#logging.basicConfig
Петр Доброгост
2

Я не очень хорошо знаю модуль регистрации, но я использую его так, как обычно хочу отключить только сообщения отладки (или информацию). Вы можете использовать, Handler.setLevel()чтобы установить уровень регистрации на КРИТИЧЕСКИЙ или выше.

Также вы можете заменить sys.stderr и sys.stdout файлом, открытым для записи. См. Http://docs.python.org/library/sys.html#sys. стандартный вывод . Но я бы не рекомендовал это.

Краб
источник
Это может сработать, если logger.handlers будет содержать что-то, на данный момент [].
сорин
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
Добавьте описание кода. Это помогло бы намного лучше
Мэтьюз Санни
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
sonance207
источник
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 конфигурации:

      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 конфигурации:

      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
Maggyero
источник
-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()
jake77
источник