Como desativar e reativar o log do console no Python?

154

Estou usando o módulo de log do Python e quero desativar o log do console por algum tempo, mas ele não funciona.

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

O código acima exibe o bla blaon stdout e não sei como posso desativar com segurança o manipulador do console. Como posso ter certeza de remover temporariamente o StreamHandler do console e não outro?

sorin
fonte
Para aqueles que se perguntam por que alguém desejaria desativar o registro: Você não gostaria de registrar dados particulares, como senhas ou chaves de API.
precisa saber é o seguinte
4
@StevenVascellaro. Por que aqueles que estão sendo enviados para um logger então? Isso não parece certo ... #
45877 Mad Physicist
1
@ MadPhysicist Eu tenho um aplicativo que envia solicitações XML para uma API externa. Por padrão, essas solicitações são registradas em um arquivo. No entanto, o login inicial requer autenticação com um nome de usuário e senha, os quais não quero que sejam registrados.
Stevoisiak
@StevenVascellaro. Entendo. Obrigada pelo esclarecimento.
May Physicist
Você não mostra como / onde você adiciona seus manipuladores. Se eles foram adicionados ao criador de logs raiz, isso impediria o registro de adicionar StreamHandler padrão, conforme descrito em docs.python.org/3/library/logging.html#logging.basicConfig Além disso, por descrição vinculada, por descrição vinculada, o StreamHandler padrão é adicionado apenas durante a primeira mensagem de log de emissão de chamadas; portanto, quando você imprimir, logger.handlersela deverá estar vazia (pois precede a logger.debug()chamada). O código em questão é exibido apenas [](lista vazia de manipuladores). Verificado com Python 2.7.15 e Python 3.6.6.
Piotr Dobrogost

Respostas:

197

Encontrei uma solução para isso:

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

Isso impedirá que o log seja enviado ao logger superior que inclui o log do console.

sorin
fonte
8
Não acho que seja uma boa solução. Não propagar para madeireiros mais altos pode ter outras conseqüências indesejáveis.
Lfk 31/08/19
2
Se você quisesse única mensagem filtro abaixo de um certo nível de log (digamos, todas as INFOmensagens), você pode mudar a segunda linha para algo comologger.setLevel(logging.WARNING)
Hartley Brody
2
Como você reativaria o log posteriormente?
Stevoisiak
4
Não é uma resposta como bloqueando a propagação efetivamente desativa todos os manipuladores do logger raiz ea questão afirma claramente (...) mas eu pode ter outros manipuladores de lá que eu quero manter o que sugere a intenção é desativar StreamHandler padrão do logger raiz única .
Piotr Dobrogost
Parar a propagação de mensagens não é suficiente. Desde o Python 3.2 , o logging.lastResortmanipulador ainda registrará mensagens de severidade logging.WARNINGe maiores sys.stderrna ausência de outros manipuladores. Veja minha resposta .
Maggyero 20/04
106

Eu uso:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
infinito
fonte
9
isso também funciona no loggingnível do módulo para desativar completamente o registro , por exemplo import logging; logging.disable(logging.CRITICAL);:: docs.python.org/2/library/logging.html#logging.disable
lsh
1
Isso é muito melhor do que desativar a propagação.
Mátray Marár
6
Não é uma resposta - a pergunta pergunta como desativar apenas o StreamHandler padrão .
Piotr Dobrogost
1
O disabledatributo não faz parte da API pública. Veja bugs.python.org/issue36318 .
Maggyero 28/03/19
69

Você pode usar:

logging.basicConfig(level=your_level)

onde your_level é um deles:

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

Portanto, se você definir seu_level como log.CRITICAL , receberá apenas mensagens críticas enviadas por:

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

Configurando seu_level para o log.DEBUG mostrará todos os níveis de log.

Para mais detalhes, consulte exemplos de registro.

Da mesma maneira para alterar o nível de cada manipulador, use a função 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
fonte
6
Geralmente, são informações úteis, mas a pergunta foi feita sobre como desativar o log do console, e não sobre como adicionar um manipulador adicional. se você examinasse my_logger.handlers com o código acima aplicado ao exemplo original, verá dois manipuladores - seu novo manipulador de arquivos e o manipulador de fluxo original.
21417 Joe
CRÍTICA era a palavra que eu estava procurando. Obrigado.
Nishant
Eu adoraria ver um nível de depuração desativado. É simples e inequívoco.
Não uma máquina
46

(pergunta há muito morta, mas para futuros pesquisadores)

Mais próximo do código / intenção do pôster original, isso funciona para mim no 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")

O problema que tive que resolver foi remover o manipulador stdout após adicionar um novo; o código do criador de logs parece adicionar novamente o stdout automaticamente se nenhum manipulador estiver presente.

l82ky
fonte
2
A sequência logger = logging.getLogger(); lhStdout = logger.handlers[0]está incorreta, pois o registrador raiz inicialmente não possui manipuladores - python -c "import logging; assert not logging.getLogger().handlers". Verificado com Python 2.7.15 e Python 3.6.6.
Piotr Dobrogost
42

Gerenciador de contexto

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

Exemplo de uso:

with DisableLogger():
    do_something()
pymen
fonte
Eu realmente gosto desse idioma, mas preferiria desativar um espaço para nome específico. Por exemplo, eu quero apenas o logger raiz desativado temporariamente. Apesar de usar esse idioma, devemos poder adicionar / remover temporariamente os manipuladores e tal.
12284 Chris
1
A pergunta pergunta como desativar apenas o StreamHandler padrão .
Piotr Dobrogost
1
Você não precisa rolar sua própria classe, você pode usar @contextmanager de contextlib e escrever uma função rendendo
KristianR
Se você gosta de frutas exóticas na sua pizza. Certo.
user3504575
34

Para desativar totalmente o log :

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

Para habilitar o log :

logging.disable(logging.NOTSET)

Outras respostas fornecem soluções alternativas que não resolvem completamente o problema, como

logging.getLogger().disabled = True

e, para alguns nsuperiores a 50,

logging.disable(n)

O problema com a primeira solução é que ele funciona apenas para o registrador raiz. Outros criadores de logs criados usando, por exemplo, logging.getLogger(__name__)não são desativados por esse método.

A segunda solução afeta todos os logs. Mas limita a saída a níveis acima do fornecido, para que você possa substituí-la registrando-se em um nível superior a 50.

Isso pode ser evitado por

logging.disable(sys.maxint)

que até onde eu sei (depois de revisar a fonte ) é a única maneira de desativar completamente o log.

starfry
fonte
1
Downvote como a pergunta é como StreamHandler padrão desativar única
Piotr Dobrogost
27

Existem algumas respostas muito legais aqui, mas aparentemente a mais simples não é levada muito em consideração (apenas a partir do infinito).

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

Isso desativa o registrador raiz e, portanto, todos os outros registradores. Eu realmente não testei, mas também deve ser o mais rápido.

No código de log do python 2.7, vejo isso

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)

O que significa que, quando desabilitado, nenhum manipulador é chamado e deve ser mais eficiente filtrar para um valor muito alto ou definir um manipulador não operacional, por exemplo.

andrea_crotti
fonte
1
A menos que eu estou fazendo algo errado, isso só desativa o logger raiz e não qualquer criado comolog = logging.getLogger(__name__)
starfry
2
Isso pode ser problemático se você estiver lidando com vários registradores ou manipuladores. Se, por exemplo, você ainda deseja efetuar logon em um arquivo, mas deseja desativar o manipulador de fluxo em um caso específico.
21417 Joe Joe
1
Isso desativa o registrador raiz e, portanto, todos os outros registradores - estritamente falando, desativar o registrador raiz não desativa nenhum outro registrador. Além da pergunta pergunta sobre como desativar apenas StreamHandler padrão .
Piotr Dobrogost
O disabledatributo não faz parte da API pública. Veja bugs.python.org/issue36318 .
Maggyero 28/03/19
10

Não há necessidade de desviar o desvio padrão. Aqui está a melhor maneira de fazer isso:

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

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

Uma maneira ainda mais simples é:

logging.getLogger().setLevel(100)
Ehsan Foroughi
fonte
4
Em Python 2.7+ este está disponível como NullHandler ()
Pierre
1
A razão pela qual isso funciona (desabilita o StreamHandler padrão) pode ser vista ao ler a descrição da logging.basicConfig()função (ênfase minha): Faz a configuração básica do sistema de log criando um StreamHandler com um Formatador padrão e adicionando-o ao registrador raiz. As funções debug (), info (), warning (), error () e critical () chamarão basicConfig () automaticamente se nenhum manipulador estiver definido para o registrador raiz . - docs.python.org/3/library/logging.html#logging.basicConfig
Piotr Dobrogost
2

Não conheço o módulo de registro muito bem, mas estou usando-o da maneira que geralmente quero desativar apenas as mensagens de depuração (ou informações). Você pode usar Handler.setLevel()para definir o nível de log como CRÍTICO ou superior.

Além disso, você pode substituir sys.stderr e sys.stdout por um arquivo aberto para gravação. Consulte http://docs.python.org/library/sys.html#sys. stdout . Mas eu não recomendaria isso.

Siri
fonte
Isso poderia funcionar se logger.handlers contivesse algo, atualmente é [].
sorin
2

Você também pode:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers
Italo Maia
fonte
Por que você está usando o app.loggerque você nem especifica em vez do registrador raiz mencionado explicitamente na pergunta ( logging.getLogger()) e na maioria das respostas? Como você sabe que pode modificar a handlerspropriedade com segurança em vez de chamar o Logger.addHandlermétodo?
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')

Saída do console:

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

conteúdo do arquivo 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
fonte
2
Adicione alguma descrição sobre o código. Ajudaria muito melhor
Mathews Ensolarado
2

Alterando um nível no "logging.config.dictConfig", você poderá levar todo o nível de log para um novo nível.

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

Encontrou uma solução elegante usando decoradores , que soluciona o seguinte problema: e se você estiver escrevendo um módulo com várias funções, cada uma com várias mensagens de depuração, e quiser desativar o log de todas as funções, exceto aquela em que está focando atualmente?

Você pode fazer isso usando decoradores:

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

Então, você pode fazer:

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

Mesmo se você ligar function_already_debuggedde dentro function_being_focused, as mensagens de depuração de function_already_debuggednão serão exibidas. Isso garante que você verá apenas as mensagens de depuração da função em que está se concentrando.

Espero que ajude!

Carlos Souza
fonte
0

Se você deseja desativar temporariamente um certo criador de logs, aqui está o que foi feito.

Exemplo de log

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

Código

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

Na biblioteca Logging Python, é possível desativar completamente o log (para todos os níveis) de um logger específico, executando um dos seguintes procedimentos:

  1. Incluindo no criador de logs um logging.NullHandler()manipulador (para impedir que o logging.lastResortmanipulador registre eventos de gravidade logging.WARNINGe maiores que sys.stderr) e configurando o propagateatributo desse criador de logs paraFalse (para impedir que o criador de logs passe os eventos para os manipuladores dos criadores de logs ancestrais).

    • Usando a API principal:

      import logging
      
      logging.getLogger("foo").addHandler(logging.NullHandler())
      logging.getLogger("foo").propagate = False
    • Usando a API de configuração:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "handlers": {
              "null": {
                  "class": "logging.NullHandler"
              }
          },
          "loggers": {
              "foo": {
                  "handlers": ["null"],
                  "propagate": False
              }
          }
      })
  2. Adicionando ao criador de logs um lambda record: Falsefiltro.

    • Usando a API principal:

      import logging
      
      logging.getLogger("foo").addFilter(lambda record: False)
    • Usando a API de configuração:

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

Aviso. - Ao contrário da 1ª solução, a 2ª solução não desabilita o log de registradores filhos (por exemplo logging.getLogger("foo.bar")), portanto, deve-se usá-lo apenas para desativar o log de um único logger.

Nota. - Definir o disabledatributo do criador de logs como Truenão é uma terceira solução, pois não faz parte da API pública. Consulte https://bugs.python.org/issue36318 :

import logging

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

subclasse o manipulador que você deseja desativar temporariamente:

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)

encontrar o manipulador pelo nome é bastante fácil:

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

uma vez encontrado:

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