Python Logging - Desative o registro de módulos importados

107

Estou usando o módulo de registro Python e gostaria de desativar as mensagens de log impressas pelos módulos de terceiros que importei. Por exemplo, estou usando algo como o seguinte:

logger = logging.getLogger()
logger.setLevel(level=logging.DEBUG)
fh = logging.StreamHandler()
fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
fh.setFormatter(fh_formatter)
logger.addHandler(fh)

Isso imprime minhas mensagens de depuração quando eu faço um logger.debug ("minha mensagem!"), Mas também imprime as mensagens de depuração de qualquer módulo que eu importo (como solicitações e várias outras coisas).

Gostaria de ver apenas as mensagens de log dos módulos nos quais estou interessado. É possível fazer com que o módulo de log faça isso?

Idealmente, eu gostaria de ser capaz de dizer ao logger para imprimir mensagens de "ModuleX, ModuleY" e ignorar todas as outras.

Observei o seguinte, mas não quero ter que desabilitar / habilitar o registro antes de cada chamada para uma função importada: registro - como ignorar os registros do módulo importado?

blindsnowmobile
fonte

Respostas:

76

O problema é que a chamada getLoggersem argumentos retorna o logger raiz, portanto, quando você define o nível como, logging.DEBUGtambém está configurando o nível para outros módulos que usam esse logger.

Você pode resolver isso simplesmente não usando o logger root. Para fazer isso, basta passar um nome como argumento, por exemplo o nome do seu módulo:

logger = logging.getLogger('my_module_name')
# as before

isso criará um novo registrador e, portanto, não alterará inadvertidamente o nível de registro de outros módulos.


Obviamente, você deve usar em logger.debugvez de, logging.debugpois o último é uma função de conveniência que chama o debugmétodo do logger raiz.

Isso é mencionado no Tutorial de registro avançado . Ele também permite que você saiba qual módulo disparou a mensagem de log de forma simples.

Bakuriu
fonte
38
Estou criando um logger com __name__r, mas ainda vejo os logs dos módulos importados. Estou tentando configurar o log com um arquivo de configuração ini. O que devo fazer para isso?
Durga Swaroop
8
Criar um logger com __name__também não funcionou para mim. Talvez porque estou usando um script autônomo e não um "módulo"? O que funcionou para mim foi configurar o log de módulos importados ( matpplotlibno meu caso) via logging.getLogger("matplotlib").setLevel(logging.WARNING)e para meu script via logging.basicConfig.
bli
1
Eu só queria destacar o valor de sua linha "Obviamente, você deve usar em logger.debugvez de logging.debug". É um erro fácil de cometer ao usar o log em vez do logger, mas usurpa todas as configurações inteligentes que você deseja definir. Passei as últimas horas vivendo isso!
timdadev
@bli Existe uma grande diferença entre registrar ao desenvolver uma biblioteca e ao desenvolver um executável. Basicamente: se você está escrevendo um módulo / pacote que deve ser importado, você não deve configurar nada. O módulo deve conter apenas as logger = logging.getLogger('package.my_module')chamadas e your could logger.debug/warning, sem configuração de níveis de registro ou manipuladores. Ao escrever o aplicativo binário lá, você deve decidir o nível dos vários logs e dos manipuladores. Bibliotecas que contêm configuração de registro sempre serão um problema.
Bakuriu
1
@IanS Não, é por isso que as bibliotecas não devem usar o logger root. Abra um relatório de bug para essa biblioteca e diga a eles para usarem logging.getLogger(__name__).
Bakuriu
48

Se você for usar o loggingpacote python , é uma convenção comum definir um logger em cada módulo que o usa.

logger = logging.getLogger(__name__)

Muitos pacotes populares de python fazem isso, incluindo requests. Se um pacote usa esta convenção, é fácil habilitar / desabilitar o log para ele, porque o nome do logger será o mesmo nome do pacote (ou será um filho desse logger). Você pode até mesmo registrá-lo no mesmo arquivo que seus outros registradores.

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

requests_logger = logging.getLogger('requests')
requests_logger.setLevel(logging.DEBUG)

handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
requests_logger.addHandler(handler)
Brendan Abel
fonte
16
Esteja ciente de que quando você tentar configurar seus loggers como no tutorial básico oficial com logging.basicConfig(...)todos os loggers, você terá uma saída para logging.lastResort(começando com Python 3.2, que é stderr) se nenhum manipulador foi fornecido ou para o manipulador definido. Portanto, não o use ou você continuará recebendo todas as mensagens de log de qualquer maneira.
user136036 de
44

Não tenho certeza se isso é apropriado para postar, mas eu estava preso por um longo tempo e queria ajudar qualquer pessoa com o mesmo problema, já que não tinha encontrado em nenhum outro lugar!

Eu estava obtendo logs de depuração do matplotlib, apesar de seguir a documentação bastante direta no tutorial de log avançado e solução de problemas . Eu estava iniciando meu logger em main()um arquivo e importando uma função para criar um gráfico de outro arquivo (onde importei matplotlib).

O que funcionou para mim foi definir o nível de matplotlib antes de importá-lo, ao invés de depois, como eu tinha feito para outros módulos em meu arquivo principal. Isso parecia contra-intuitivo para mim, então se alguém tiver uma ideia de como você pode definir a configuração de um logger que ainda não foi importado, eu ficaria curioso para descobrir como isso funciona. Obrigado!

No meu arquivo principal:

import logging
import requests
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logging.getLogger('requests').setLevel(logging.DEBUG)

def main():
  ...

Em meu plot.pyarquivo:

import logging
logging.getLogger('matplotlib').setLevel(logging.WARNING)
import matplotlib.pyplot as plt

def generatePlot():
  ...
Finn
fonte
Recebi o erro: o objeto 'Logger' não tem o atributo 'DEBUG'. logger.DEBUGdeveria serlogging.DEBUG
foxiris
Obrigado! Isso realmente ajuda! Eu defino o nível de registro matplotlib após minha configuração de registro principal e antes do comando que importará matplotlib. Resolvido!
gph
Eu configurei o registo para matplotliba WARNING depois que eu importados do módulo, uma vez adicionando-o antes de importar daria um erro fiapos. Ainda funcionou para mim. Estou usando matplotlib==3.3.2em Python 3.7 se isso ajudar.
Fim-2-Fim
10

Isso desativa todos os registradores existentes, como aqueles criados por módulos importados, enquanto ainda usa o registrador raiz (e sem ter que carregar um arquivo externo).

import logging.config
logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': True,
})

Observe que você precisa importar todos os módulos que não deseja que sejam registrados primeiro! Caso contrário, eles não serão considerados como "registradores existentes". Em seguida, desativará todos os registradores desses módulos. Isso pode fazer com que você também perca erros importantes!

Para obter exemplos mais detalhados usando opções relacionadas para configuração, consulte https://gist.github.com/st4lk/6287746 e aqui está um exemplo (parcialmente funcional) usando YAML para configuração com a coloredlogbiblioteca.

avv
fonte
Qual é a sua dúvida?
user1767754
1
Isso funciona por requestexemplo, mas não funcionará quando os módulos importados criarem seus loggers dentro de sua classe que você chamaria mais tarde, como o APSchedulerfaz quando você chama BackgroundScheduler.BackgroundScheduler(). Veja aqui uma solução: stackoverflow.com/a/48891485/2441026
user136036
Isso funciona para o meu caso com o arquivo de configuração
yaml
Observe que você precisa import logging.config, não apenas registrar.
user1717828
9

@Bakuriu explica elegantemente a função. Por outro lado, você pode usar o getLogger()método para recuperar e reconfigurar / desativar os registradores indesejados.

Eu também queria adicionar o logging.fileConfig()método aceita um parâmetro chamado disable_existing_loggersque irá desabilitar quaisquer registradores previamente definidos (ou seja, em módulos importados).

senhor do meme
fonte
4

Você poderia usar algo como:

logging.getLogger("imported_module").setLevel(logging.WARNING)
logging.getLogger("my_own_logger_name").setLevel(logging.DEBUG)

Isso definirá o nível de registro do meu próprio módulo como DEBUG, enquanto evita que o módulo importado use o mesmo nível.

Nota: "imported_module"pode ser substituído por imported_module.__name__(sem aspas), e "my_own_logger_name"pode ser substituído por __name__se essa for a maneira que você preferir.

Kamiku
fonte
1

Eu tive o mesmo problema. Eu tenho um arquivo logging_config.py que importo em todos os outros arquivos py. No arquivo logging_config.py, eu defino o nível de registro do logger root como ERROR (por padrão é o aviso):

logging.basicConfig(
    handlers=[
        RotatingFileHandler('logs.log',maxBytes=1000, backupCount=2),
        logging.StreamHandler(), #print to console
    ],
    level=logging.ERROR
)

Em outros módulos, eu importo o logging_config.py e declaro um novo registrador e defino seu nível para depuração:

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

Desta forma, tudo que eu logar em meus arquivos py é registrado, mas o material registrado no nível de depuração e informação por módulos importados como urllib, request, boto3 etc não é registrado. Se houver algum erro nesse módulo de importação, ele será registrado, uma vez que configurei o nível dos loggers de raiz para ERROR.

Aseem
fonte
1

Outra coisa a considerar é a propagação propriedade de da classe Logger.

Por exemplo, a biblioteca py-suds para lidar com chamadas de sabão, mesmo colocada em ERROR

logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)

logs logs sobre um módulo chamado sxbasics.py criaçãog uma grande quantidade de logs

insira a descrição da imagem aqui

Isso porque a propagação dos logs é True por padrão, configurando como False, em vez disso, recuperei 514 MB de logs.

import logging
logging.getLogger("suds").propagate = False
logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)
Andrea Bisello
fonte
1

Simplesmente fazer algo assim resolve o problema:

logging.config.dictConfig({'disable_existing_loggers': True,})

NOTA : Onde quer que você esteja colocando esta linha, certifique-se de que todas as importações em todo o seu projeto sejam feitas dentro do próprio arquivo. :)

MaxCode
fonte
0

No meu caso, a única coisa que ajudou foi forçar um propagateatributo de logger indesejado para False, ou seja,

logging.getLogger("module").propagate = False

Se isso for avaliado como falso, as mensagens de registro não são passadas para os manipuladores de registradores ancestrais.

Tomasz Bartkowiak
fonte