Jak wyłączyć i ponownie włączyć logowanie konsoli w Pythonie?

154

Używam modułu logowania Pythona i chcę wyłączyć rejestrowanie konsoli na jakiś czas, ale to nie działa.

#!/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")

Powyższy kod wyświetla bla blana stdout i nie wiem, jak bezpiecznie wyłączyć obsługę konsoli. Jak mogę się upewnić, że tymczasowo usunę konsolę StreamHandler, a nie inną?

sorin
źródło
Dla tych, którzy zastanawiają się, dlaczego ktoś miałby chcieć wyłączyć rejestrowanie: nie chciałbyś rejestrować prywatnych danych, takich jak hasła lub klucze API.
Stevoisiak
4
@StevenVascellaro. Dlaczego więc są one wysyłane do rejestratora w pierwszej kolejności? To nie brzmi dobrze ...
Mad Physicist,
1
@MadPhysicist Mam aplikację, która wysyła żądania XML do zewnętrznego API. Domyślnie te żądania są rejestrowane w pliku. Jednak pierwsze logowanie wymaga uwierzytelnienia za pomocą nazwy użytkownika i hasła, których nie chcę, aby były logowane.
Stevoisiak
@StevenVascellaro. Widzę. Dziękuję za wyjaśnienie.
Mad Physicist
Nie pokazujesz, jak / gdzie dodajesz swoje programy obsługi. Gdyby zostały dodane do głównego programu rejestrującego, uniemożliwiłoby to dodanie domyślnego StreamHandlera do logowania, jak opisano na docs.python.org/3/library/logging.html#logging.basicConfig Ponadto, zgodnie z połączonym opisem, domyślny StreamHandler jest dodawany tylko na początku komunikat dziennika emisji połączeń, więc podczas drukowania logger.handlerspowinien być pusty (tak jak poprzedza logger.debug()wywołanie). Kod, o którym mowa, jest wyświetlany tylko [](pusta lista programów obsługi). Zweryfikowano w Pythonie 2.7.15 i Pythonie 3.6.6.
Piotr Dobrogost

Odpowiedzi:

197

Znalazłem na to rozwiązanie:

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

Zapobiegnie to wysyłaniu rejestrowania do wyższego programu rejestrującego, który obejmuje rejestrowanie konsoli.

sorin
źródło
8
Myślę, że to nie jest dobre rozwiązanie. Brak propagacji do wyższych rejestratorów może mieć inne niepożądane konsekwencje.
Lfk
2
Jeśli chcesz filtrować tylko wiadomości poniżej pewnego poziomu dziennika (powiedzmy, wszystkie INFOwiadomości), możesz zmienić drugą linię na coś w rodzajulogger.setLevel(logging.WARNING)
Hartley Brody.
2
Jak możesz później ponownie włączyć dziennik?
Stevoisiak
4
Brak odpowiedzi, ponieważ blokowanie propagacji skutecznie wyłącza wszystkie programy obsługi głównego rejestratora, a pytanie wyraźnie stwierdza (…), ale mogę mieć tam inne programy obsługi, które chcę zachować, co sugeruje, że intencją jest wyłączenie domyślnego StreamHandler tylko głównego rejestratora .
Piotr Dobrogost
Zatrzymanie propagacji wiadomości nie wystarczy. Od Pythona 3.2 The logging.lastResorthandler będzie jeszcze wiadomości nasilenia zalogować logging.WARNINGi większej do sys.stderrw braku innych koparki. Zobacz moją odpowiedź .
Maggyero
106

Używam:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
infinito
źródło
9
działa to również na loggingpoziomie modułu, aby całkowicie wyłączyć logowanie , na przykład import logging; logging.disable(logging.CRITICAL);:: docs.python.org/2/library/logging.html#logging.disable
lsh
1
Jest to o wiele lepsze niż wyłączenie propagacji.
Mátray Márk
6
Nie odpowiedź - pytanie pyta, jak wyłączyć domyślną StreamHandler tylko .
Piotr Dobrogost
1
disabledAtrybut nie jest częścią publicznych API. Zobacz bugs.python.org/issue36318 .
Maggyero
69

Możesz użyć:

logging.basicConfig(level=your_level)

gdzie twój_poziom jest jednym z tych:

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

Więc jeśli ustawisz your_level na logging.CRITICAL , dostaniesz tylko krytyczne komunikaty wysyłane przez:

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

Oprawa your_level na logging.DEBUG pokaże wszystkie poziomy logowania.

Aby uzyskać więcej informacji, zapoznaj się z przykładami rejestrowania.

W ten sam sposób, aby zmienić poziom dla każdego programu obsługi, użyj funkcji 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
źródło
6
Są to ogólnie przydatne informacje, ale pytanie dotyczyło sposobu wyłączenia logowania konsoli, a nie dodania dodatkowego modułu obsługi. gdybyś zbadał my_logger.handlers z powyższym kodem zastosowanym do oryginalnego przykładu, zobaczyłbyś dwie funkcje obsługi - nową procedurę obsługi plików i oryginalną procedurę obsługi strumienia.
Joe
KRYTYCZNY to słowo, którego szukałem. Dzięki.
Nishant
Chciałbym zobaczyć poziom debugowania OFF. Jest to jednoznaczne i proste.
To nie jest maszyna
46

(od dawna martwe pytanie, ale dla przyszłych poszukiwaczy)

Bliżej kodu / intencji oryginalnego postera, działa to dla mnie pod Pythonem 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")

Problem, który musiałem rozwiązać, polegał na usunięciu programu obsługi standardowego wyjścia po dodaniu nowego; wydaje się, że kod rejestratora automatycznie dodaje ponownie standardowe wyjście, jeśli nie ma żadnych programów obsługi.

l82ky
źródło
2
Sekwencja logger = logging.getLogger(); lhStdout = logger.handlers[0]jest nieprawidłowa, ponieważ główny program rejestrujący początkowo nie ma programów obsługi - python -c "import logging; assert not logging.getLogger().handlers". Zweryfikowano w Pythonie 2.7.15 i Pythonie 3.6.6.
Piotr Dobrogost
42

Menedżer kontekstu

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

Przykład użycia:

with DisableLogger():
    do_something()
pymen
źródło
Bardzo podoba mi się ten idiom, ale wolałbym mieć możliwość wyłączenia określonej przestrzeni nazw. Na przykład chcę tylko tymczasowo wyłączyć główny program rejestrujący. Chociaż używamy tego idiomu, powinniśmy móc tylko tymczasowo dodawać / usuwać programy obsługi i tym podobne.
Chris,
1
Pytanie dotyczy sposobu wyłączenia domyślnego tylko StreamHandler .
Piotr Dobrogost
1
Nie musisz tworzyć własnej klasy, możesz użyć @contextmanager z biblioteki contextlib i napisać funkcję dającą wynik
KristianR
Jeśli lubisz egzotyczne owoce na swojej pizzy. Pewnie.
user3504575
34

Aby całkowicie wyłączyć rejestrowanie :

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

Aby włączyć rejestrowanie :

logging.disable(logging.NOTSET)

Inne odpowiedzi zawierają obejścia, które nie rozwiązują w pełni problemu, na przykład

logging.getLogger().disabled = True

a dla niektórych npowyżej 50 lat

logging.disable(n)

Problem z pierwszym rozwiązaniem polega na tym, że działa tylko dla głównego programu rejestrującego. Inne loggery utworzone za pomocą, powiedzmy, logging.getLogger(__name__)nie są wyłączane przez tę metodę.

Drugie rozwiązanie ma wpływ na wszystkie dzienniki. Ale ogranicza wyjście do poziomów powyżej podanego, więc można je zastąpić, logując się z poziomem większym niż 50.

Można temu zapobiec

logging.disable(sys.maxint)

co, o ile wiem (po przejrzeniu źródła ), jest jedynym sposobem na całkowite wyłączenie logowania.

starfry
źródło
1
Głosuj w dół, bo pytanie dotyczy wyłączania tylko
Piotr Dobrogost
27

Jest tu kilka naprawdę fajnych odpowiedzi, ale najwyraźniej nie bierze się zbytnio pod uwagę najprostszych (tylko z infinito).

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

Spowoduje to wyłączenie głównego programu rejestrującego, a tym samym wszystkich innych programów rejestrujących. Tak naprawdę nie testowałem, ale powinien być też najszybszy.

Widzę to z kodu logowania w Pythonie 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)

Co oznacza, że ​​gdy jest wyłączona, żaden program obsługi nie jest wywoływany i powinno być bardziej wydajne niż na przykład filtrowanie do bardzo wysokiej wartości lub ustawianie funkcji obsługi nieobsługiwanej.

andrea_crotti
źródło
1
O ile nie robię czegoś źle, wyłącza to tylko rejestrator roota, a nie każdy utworzony jaklog = logging.getLogger(__name__)
starfry
2
Może to być problematyczne, jeśli masz do czynienia z wieloma rejestratorami lub wieloma programami obsługi. Jeśli na przykład nadal chcesz zalogować się do pliku, ale chcesz wyłączyć obsługę strumienia w określonym przypadku.
Joe,
1
Spowoduje to wyłączenie głównego programu rejestrującego, a tym samym wszystkich innych programów rejestrujących - mówiąc ściśle, wyłączenie głównego programu rejestrującego nie wyłącza żadnych innych programów rejestrujących. Oprócz kwestii prosi o wyłączenie domyślnego StreamHandler tylko .
Piotr Dobrogost
disabledAtrybut nie jest częścią publicznych API. Zobacz bugs.python.org/issue36318 .
Maggyero
10

Nie ma potrzeby przekierowywania wyjścia standardowego. Oto lepszy sposób na zrobienie tego:

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

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

Jeszcze prostszym sposobem jest:

logging.getLogger().setLevel(100)
Ehsan Foroughi
źródło
4
W Pythonie 2.7+ jest to dostępne jako NullHandler ()
Pierre
1
Powód, dla którego to działa (wyłącza domyślny StreamHandler), można zobaczyć podczas czytania opisu logging.basicConfig()funkcji (moje podkreślenie): Czy podstawowa konfiguracja systemu logowania polega na utworzeniu StreamHandler z domyślnym formaterem i dodaniu go do głównego rejestratora. Funkcje debug (), info (), warning (), error () i krytyczne () będą automatycznie wywoływać basicConfig (), jeśli dla głównego programu rejestrującego nie są zdefiniowane żadne funkcje obsługi . - docs.python.org/3/library/logging.html#logging.basicConfig
Piotr Dobrogost
2

Nie znam zbyt dobrze modułu logowania, ale używam go w sposób, w jaki zwykle chcę wyłączyć tylko komunikaty debugowania (lub informacje). Możesz użyćHandler.setLevel() aby ustawić poziom rejestrowania na KRYTYCZNY lub wyższy.

Można również zastąpić sys.stderr i sys.stdout plikiem otwartym do zapisu. Zobacz http://docs.python.org/library/sys.html#sys. stdout . Ale nie polecałbym tego.

Krab
źródło
To mogłoby zadziałać, gdyby plik logger.handlers zawierał coś, co obecnie jest [].
sorin
2

Możesz również:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers
Italo Maia
źródło
Dlaczego używasz, app.loggerktórego nawet nie określasz, zamiast głównego programu rejestrującego wyraźnie wymienionego w pytaniu ( logging.getLogger()) i większości odpowiedzi? Skąd wiesz, że możesz bezpiecznie modyfikować handlerswłaściwość zamiast wywoływać Logger.addHandlermetodę?
Piotr Dobrogost
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')

Wyjście konsoli:

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

Zawartość pliku 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
Shawn Hu
źródło
2
Dodaj opis kodu. Pomogłoby to znacznie lepiej
Mathews Sunny
2

Zmieniając jeden poziom w pliku „logging.config.dictConfig”, można przenieść cały poziom rejestrowania na nowy poziom.

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,
    },
}

})

Ganesh Kalidas
źródło
1

Znalazłem eleganckie rozwiązanie przy użyciu dekoratorów , które rozwiązuje następujący problem: co jeśli piszesz moduł z kilkoma funkcjami, z których każda zawiera kilka komunikatów debugowania, i chcesz wyłączyć logowanie wszystkich funkcji oprócz tej, na której się obecnie koncentrujesz?

Możesz to zrobić za pomocą dekoratorów:

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

Następnie możesz:

@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")
    ...

Nawet jeśli dzwonisz function_already_debuggedz wewnątrz function_being_focused, debuguj wiadomości odfunction_already_debugged adresu nie będą wyświetlane. Gwarantuje to, że zobaczysz tylko komunikaty debugowania z funkcji, na której się koncentrujesz.

Mam nadzieję, że to pomoże!

Carlos Souza
źródło
0

Jeśli chcesz tymczasowo wyłączyć określony rejestrator, oto co zostało zrobione.

Przykładowy dziennik

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

Kod

django_request_logger = logging.getLogger('django.request')
django_request_logger.disabled = True
django_request_logger.disabled = False
sonance207
źródło
0

W bibliotece Logging Python można całkowicie wyłączyć rejestrowanie (na wszystkich poziomach) dla określonego programu rejestrującego, wykonując jedną z następujących czynności:

  1. Dodanie do programu rejestrującego programu logging.NullHandler()obsługi (aby zapobiec logging.lastResortrejestrowaniu przez program obsługi zdarzeń o istotności logging.WARNINGi wyższym sys.stderr) i ustawienie propagateatrybutu tego programu rejestrującego naFalse (aby uniemożliwić rejestratorowi przekazywanie zdarzeń do programów obsługi jego nadrzędnych rejestratorów).

    • Korzystanie z głównego interfejsu API:

      import logging
      
      logging.getLogger("foo").addHandler(logging.NullHandler())
      logging.getLogger("foo").propagate = False
    • Korzystanie z config API:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "handlers": {
              "null": {
                  "class": "logging.NullHandler"
              }
          },
          "loggers": {
              "foo": {
                  "handlers": ["null"],
                  "propagate": False
              }
          }
      })
  2. Dodanie do loggera lambda record: Falsefiltra.

    • Korzystanie z głównego interfejsu API:

      import logging
      
      logging.getLogger("foo").addFilter(lambda record: False)
    • Korzystanie z config API:

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

Ostrzeżenie. - W przeciwieństwie do rozwiązania pierwszego, rozwiązanie drugie nie wyłącza logowania z loggerów podrzędnych (nplogging.getLogger("foo.bar") loggerów ), Więc należy go używać tylko do wyłączania logowania dla pojedynczego loggera.

Uwaga. - Ustawienie disabledatrybutu loggera na Truenie jest trzecim rozwiązaniem, ponieważ nie jest częścią publicznego API. Zobacz https://bugs.python.org/issue36318 :

import logging

logging.getLogger("foo").disabled = True  # DO NOT DO THAT
Maggyero
źródło
-1

podklasę moduł obsługi, który chcesz tymczasowo wyłączyć:

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)

znalezienie programu obsługi po imieniu jest dość łatwe:

_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))

raz znaleziony:

_handler.disable()
doStuff()
_handler.enable()
jake77
źródło