Wie kann ich die Konsolenprotokollierung in Python deaktivieren und wieder aktivieren?

153

Ich verwende das Protokollierungsmodul von Python und möchte die Konsolenprotokollierung für einige Zeit deaktivieren, aber es funktioniert nicht.

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

Der obige Code zeigt das bla blaon stdout an und ich weiß nicht, wie ich den Konsolenhandler sicher deaktivieren kann. Wie kann ich sicher sein, dass ich den StreamHandler der Konsole vorübergehend entferne und keinen anderen?

Sorin
quelle
Für diejenigen, die sich fragen, warum jemand die Protokollierung deaktivieren möchte: Sie möchten keine privaten Daten wie Kennwörter oder API-Schlüssel protokollieren.
Stevoisiak
4
@StevenVascellaro. Warum werden diese dann überhaupt an einen Logger gesendet? Das klingt nicht richtig ...
Mad Physicist
1
@ MadPhysicist Ich habe eine Anwendung, die XML-Anforderungen an eine externe API sendet. Standardmäßig werden diese Anforderungen in einer Datei protokolliert. Die anfängliche Anmeldung erfordert jedoch eine Authentifizierung mit einem Benutzernamen und einem Kennwort, die nicht protokolliert werden sollen.
Stevoisiak
@StevenVascellaro. Aha. Danke für die Erklärung.
Verrückter Physiker
Sie zeigen nicht, wie / wo Sie Ihre Handler hinzufügen. Wenn sie in dem Stamm Logger hinzugefügt wurden diese Protokollierung von Hinzufügen von Standard StreamHandler bei wie beschrieben verhindern würde docs.python.org/3/library/logging.html#logging.basicConfig Auch pro verknüpfte Beschreibung, wird der Standard StreamHandler nur während der ersten hinzugefügt Anruf emittierende Protokollnachricht, daher sollte sie beim Drucken logger.handlersleer sein (wie vor dem logger.debug()Anruf). Der betreffende Code wird nur angezeigt [](leere Liste der Handler). Verifiziert mit Python 2.7.15 und Python 3.6.6.
Piotr Dobrogost

Antworten:

197

Ich habe eine Lösung dafür gefunden:

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

Dadurch wird verhindert, dass die Protokollierung an den oberen Protokollierer gesendet wird, der die Konsolenprotokollierung enthält.

Sorin
quelle
8
Ich denke nicht, dass dies eine gute Lösung ist. Die Nichtweitergabe an höhere Logger kann andere unerwünschte Folgen haben.
lfk
2
Wenn Sie nur Filter Nachricht unter einem bestimmten Protokollebene (sagen, alle wollten INFONachrichten), können Sie die zweite Zeile etwas wie ändernlogger.setLevel(logging.WARNING)
Hartley Brody
2
Wie würden Sie das Protokoll anschließend wieder aktivieren?
Stevoisiak
4
Keine Antwort als Sperrausbreitung effektiv deaktiviert alle Handler der Root - Logger und die Frage eindeutig Staaten (...) , aber ich kann andere Handler dort hat , dass ich halten will was darauf hindeutet , die Absicht zu deaktivieren Standard StreamHandler des Root - Logger ist nur .
Piotr Dobrogost
Das Stoppen der Nachrichtenverbreitung reicht nicht aus. Seit Python 3.2logging.lastResort protokolliert der Handler auch Nachrichten mit Schweregrad logging.WARNINGund höher, sys.stderrwenn keine anderen Handler vorhanden sind. Siehe meine Antwort .
Maggyero
106

Ich benutze:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
infinito
quelle
9
Dies funktioniert auch auf der loggingModulebene zur Deaktivierung der Protokollierung vollständig , zum Beispiel: import logging; logging.disable(logging.CRITICAL);: docs.python.org/2/library/logging.html#logging.disable
lsh
1
Dies ist viel besser als das Deaktivieren der Weitergabe.
Mátray Márk
6
Keine Antwort - In der Frage wird gefragt, wie nur der Standard-StreamHandler deaktiviert werden soll .
Piotr Dobrogost
1
Das disabledAttribut ist nicht Teil der öffentlichen API. Siehe bugs.python.org/issue36318 .
Maggyero
69

Sie können verwenden:

logging.basicConfig(level=your_level)

wo your_level einer von denen ist:

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

Wenn Sie also your_level auf logging.CRITICAL setzen , erhalten Sie nur kritische Nachrichten, die gesendet werden von:

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

Wenn Sie your_level auf logging.DEBUG setzen, werden alle Protokollierungsstufen angezeigt .

Weitere Informationen finden Sie in den Protokollierungsbeispielen.

Verwenden Sie auf die gleiche Weise die Funktion Handler.setLevel () , um die Ebene für jeden Handler zu ändern .

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
quelle
6
Dies sind im Allgemeinen nützliche Informationen, aber in der Frage wurde gefragt, wie die Konsolenprotokollierung deaktiviert werden soll und nicht, wie ein zusätzlicher Handler hinzugefügt werden soll. Wenn Sie my_logger.handlers mit dem obigen Code untersuchen, der auf das ursprüngliche Beispiel angewendet wird, werden zwei Handler angezeigt - Ihr neuer Dateihandler und der ursprüngliche Stream-Handler.
Joe
KRITISCH war das Wort, nach dem ich gesucht habe. Vielen Dank.
Nishant
Ich würde gerne ein Debug-Level von OFF sehen. Es ist eindeutig und einfach.
Keine Maschine
46

(lange tote Frage, aber für zukünftige Suchende)

Näher am Code / der Absicht des Originalplakats funktioniert dies für mich unter 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")

Das Problem, das ich lösen musste, war, den Standard-Handler zu entfernen, nachdem ich einen neuen hinzugefügt hatte. Der Logger-Code scheint die Standardausgabe automatisch neu hinzuzufügen, wenn keine Handler vorhanden sind.

l82ky
quelle
2
Die Reihenfolge logger = logging.getLogger(); lhStdout = logger.handlers[0]ist falsch, da der Root-Logger zunächst keine Handler hat - python -c "import logging; assert not logging.getLogger().handlers". Verifiziert mit Python 2.7.15 und Python 3.6.6.
Piotr Dobrogost
42

Kontextmanager

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

Anwendungsbeispiel:

with DisableLogger():
    do_something()
Pymen
quelle
Ich mag diese Redewendung wirklich, aber ich würde lieber einen bestimmten Namespace deaktivieren können. Zum Beispiel möchte ich nur, dass der Root-Logger vorübergehend deaktiviert wird. Obwohl wir diese Redewendung verwenden, sollten wir in der Lage sein, Handler und dergleichen nur vorübergehend hinzuzufügen / zu entfernen.
Chris
1
In der Frage wird gefragt, wie nur der Standard-StreamHandler deaktiviert werden soll .
Piotr Dobrogost
1
Sie müssen keine eigene Klasse rollen, Sie können @contextmanager von contextlib verwenden und eine nachgebende Funktion schreiben
KristianR
Wenn Sie auf Ihrer Pizza exotische Früchte mögen. Sicher.
user3504575
34

So deaktivieren Sie die Protokollierung vollständig :

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

So aktivieren Sie die Protokollierung :

logging.disable(logging.NOTSET)

Andere Antworten bieten Problemumgehungen, die das Problem nicht vollständig lösen, wie z

logging.getLogger().disabled = True

und für einige ngrößer als 50,

logging.disable(n)

Das Problem mit der ersten Lösung ist, dass es nur für den Root-Logger funktioniert. Andere Logger, die beispielsweise mit erstellt wurden, logging.getLogger(__name__)werden mit dieser Methode nicht deaktiviert.

Die zweite Lösung betrifft alle Protokolle. Die Ausgabe wird jedoch auf Werte über dem angegebenen Wert begrenzt, sodass sie durch Protokollieren mit einem Wert über 50 überschrieben werden kann.

Das kann verhindert werden durch

logging.disable(sys.maxint)

Soweit ich das beurteilen kann (nach Überprüfung der Quelle ), ist dies die einzige Möglichkeit, die Protokollierung vollständig zu deaktivieren.

Sternenhimmel
quelle
1
Downvote als die Frage fragt, wie Standard StreamHandler nur
Piotr Dobrogost
27

Hier gibt es einige wirklich nette Antworten, aber anscheinend wird die einfachste nicht zu sehr berücksichtigt (nur von infinito).

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

Dadurch wird der Root-Logger und damit alle anderen Logger deaktiviert. Ich habe nicht wirklich getestet, aber es sollte auch das schnellste sein.

Aus dem Protokollcode in Python 2.7 sehe ich dies

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)

Das heißt, wenn es deaktiviert ist, wird kein Handler aufgerufen, und es sollte effizienter sein, als beispielsweise auf einen sehr hohen Wert zu filtern oder einen No-Op-Handler festzulegen.

andrea_crotti
quelle
1
Sofern ich nichts falsch mache, deaktiviert dies nur den Root-Logger und keine erstellten wielog = logging.getLogger(__name__)
starfry
2
Dies kann problematisch sein, wenn Sie mit mehreren Loggern oder mehreren Handlern arbeiten. Wenn Sie beispielsweise weiterhin in einer Datei protokollieren möchten, aber den Stream-Handler in einem bestimmten Fall deaktivieren möchten.
Joe
1
Dadurch wird der Root-Logger und damit alle anderen Logger deaktiviert. Wenn Sie den Root-Logger deaktivieren, werden keine anderen Logger deaktiviert. Außerdem wird gefragt, ob nur der Standard-StreamHandler deaktiviert werden soll .
Piotr Dobrogost
Das disabledAttribut ist nicht Teil der öffentlichen API. Siehe bugs.python.org/issue36318 .
Maggyero
10

Keine Notwendigkeit, stdout umzuleiten. Hier ist ein besserer Weg, dies zu tun:

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

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

Ein noch einfacherer Weg ist:

logging.getLogger().setLevel(100)
Ehsan Foroughi
quelle
4
In Python 2.7+ ist dies als NullHandler ()
Pierre
1
Der Grund, warum dies funktioniert (deaktiviert den Standard-StreamHandler), kann beim Lesen der logging.basicConfig()Funktionsbeschreibung (Hervorhebung von mir) gesehen werden: Führt die Grundkonfiguration für das Protokollierungssystem durch, indem ein StreamHandler mit einem Standardformatierer erstellt und dem Root-Logger hinzugefügt wird. Die Funktionen debug (), info (), warning (), error () und Critical () rufen basicConfig () automatisch auf, wenn für den Root-Logger keine Handler definiert sind . - docs.python.org/3/library/logging.html#logging.basicConfig
Piotr Dobrogost
2

Ich kenne das Protokollierungsmodul nicht sehr gut, aber ich verwende es so, dass ich normalerweise nur Debug- (oder Info-) Nachrichten deaktivieren möchte. Mit können Sie Handler.setLevel()die Protokollierungsstufe auf KRITISCH oder höher einstellen.

Sie können auch sys.stderr und sys.stdout durch eine zum Schreiben geöffnete Datei ersetzen. Siehe http://docs.python.org/library/sys.html#sys. stdout . Aber das würde ich nicht empfehlen.

Krabben
quelle
Dies könnte funktionieren, wenn logger.handlers etwas enthalten würde, was derzeit der Fall ist [].
Sorin
2

Du könntest auch:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers
Italo Maia
quelle
Warum verwenden Sie, app.loggerdie Sie nicht einmal angeben, anstelle des in der Frage ( logging.getLogger()) und den meisten Antworten explizit erwähnten Root-Loggers ? Woher wissen Sie, dass Sie handlersEigenschaften sicher ändern können, anstatt die Logger.addHandlerMethode aufzurufen ?
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')

Konsolenausgabe:

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

Inhalt der Datei 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
quelle
2
Fügen Sie eine Beschreibung des Codes hinzu. Es würde viel besser helfen
Mathews Sunny
2

Durch Ändern einer Ebene in "logging.config.dictConfig" können Sie die gesamte Protokollierungsstufe auf eine neue Ebene bringen.

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
quelle
1

Mit Dekoratoren eine elegante Lösung gefunden , die das folgende Problem behebt: Was ist, wenn Sie ein Modul mit mehreren Funktionen schreiben, von denen jede mehrere Debugging-Meldungen enthält, und die Anmeldung aller Funktionen außer der, auf die Sie sich gerade konzentrieren, deaktivieren möchten?

Sie können es mit Dekorateuren tun:

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

Dann können Sie Folgendes tun:

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

Auch wenn Sie function_already_debuggedvon innen anrufen function_being_focused, debuggen Sie Nachrichten vonfunction_already_debugged nicht angezeigt. Dadurch wird sichergestellt, dass nur die Debug-Meldungen der Funktion angezeigt werden, auf die Sie sich konzentrieren.

Ich hoffe es hilft!

Carlos Souza
quelle
0

Wenn Sie einen bestimmten Logger vorübergehend deaktivieren möchten, tun Sie Folgendes.

Beispielprotokoll

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

Code

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

In der Protokollierungs-Python-Bibliothek können Sie die Protokollierung (für alle Ebenen) für einen bestimmten Protokollierer vollständig deaktivieren, indem Sie einen der folgenden Schritte ausführen:

  1. Das Hinzufügen der Logger ein logging.NullHandler()Handler (verhindern , dass die logging.lastResortBehandlungsroutine von Ereignissen Schwerer Protokollierung logging.WARNINGund größer sys.stderr) und Einstellen des propagateAttributs des LoggerFalse (Logger zu verhindern , Leiten der Ereignisse an die Behandlungsroutinen des seinem Vorfahren Loggern).

    • Verwenden der Haupt-API:

      import logging
      
      logging.getLogger("foo").addHandler(logging.NullHandler())
      logging.getLogger("foo").propagate = False
    • Verwenden der Konfigurations-API:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "handlers": {
              "null": {
                  "class": "logging.NullHandler"
              }
          },
          "loggers": {
              "foo": {
                  "handlers": ["null"],
                  "propagate": False
              }
          }
      })
  2. Hinzufügen eines lambda record: FalseFilters zum Logger .

    • Verwenden der Haupt-API:

      import logging
      
      logging.getLogger("foo").addFilter(lambda record: False)
    • Verwenden der Konfigurations-API:

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

Warnung. - Im Gegensatz zur ersten Lösung deaktiviert die zweite Lösung die Protokollierung von untergeordneten Protokollierern nicht (z. B.)logging.getLogger("foo.bar") ) sollte sie nur zum Deaktivieren der Protokollierung für einen einzelnen Protokollierer verwendet werden.

Hinweis. - Das Festlegen des disabledAttributs des Protokollierers auf Trueist keine dritte Lösung, da es nicht Teil der öffentlichen API ist. Siehe https://bugs.python.org/issue36318 :

import logging

logging.getLogger("foo").disabled = True  # DO NOT DO THAT
Maggyero
quelle
-1

Unterklasse des Handlers, den Sie vorübergehend deaktivieren möchten:

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)

Es ist ganz einfach, den Handler beim Namen zu finden:

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

einmal gefunden:

_handler.disable()
doStuff()
_handler.enable()
jake77
quelle