O registro Python não produz nada

94

Em um script python que estou escrevendo, estou tentando registrar eventos usando o módulo de registro. Tenho o seguinte código para configurar meu logger:

ERROR_FORMAT = "%(levelname)s at %(asctime)s in %(funcName)s in %(filename) at line %(lineno)d: %(message)s"
DEBUG_FORMAT = "%(lineno)d in %(filename)s at %(asctime)s: %(message)s"
LOG_CONFIG = {'version':1,
              'formatters':{'error':{'format':ERROR_FORMAT},
                            'debug':{'format':DEBUG_FORMAT}},
              'handlers':{'console':{'class':'logging.StreamHandler',
                                     'formatter':'debug',
                                     'level':logging.DEBUG},
                          'file':{'class':'logging.FileHandler',
                                  'filename':'/usr/local/logs/DatabaseUpdate.log',
                                  'formatter':'error',
                                  'level':logging.ERROR}},
              'root':{'handlers':('console', 'file')}}
logging.config.dictConfig(LOG_CONFIG)

Quando tento executar logging.debug("Some string"), não obtenho nenhuma saída para o console, embora esta página nos documentos diga que logging.debugo logger root deve mostrar a mensagem. Por que meu programa não está exibindo nada e como posso corrigir isso?

murgatroid99
fonte

Respostas:

98

O nível de log padrão é aviso. Como você não alterou o nível, o nível do logger root ainda está avisando. Isso significa que ele irá ignorar qualquer registro com um nível inferior a aviso, incluindo registros de depuração.

Isso é explicado no tutorial :

import logging
logging.warning('Watch out!') # will print a message to the console
logging.info('I told you so') # will not print anything

A linha 'info' não imprime nada, porque o nível é mais alto do que info.

Para alterar o nível, basta configurá-lo no logger raiz:

'root':{'handlers':('console', 'file'), 'level':'DEBUG'}

Em outras palavras, não é suficiente definir um manipulador com nível = DEBUG, o nível de registro real também deve ser DEBUG para que ele produza qualquer coisa.

Omri Barel
fonte
6
A documentação diz que seu nível padrão é NOTSET, que é um nível 0 que deve mostrar tudo ... Por que isso não é verdade?
Ben
@Ben, onde diz isso? Tudo o que posso ver é "O nível padrão é WARNING, o que significa que apenas eventos deste nível e acima serão rastreados, a menos que o pacote de registro seja configurado para fazer o contrário."
Omri Barel
1
@Ben de acordo com os documentos que os loggers são percorridos para encontrar o primeiro pai level != NOTSETou a raiz (se nenhum for encontrado). A raiz tem WARNINGnível por padrão. Isso está escrito na seção que você vinculou a ( Logger.setLevel).
Omri Barel,
5
Lembre-se de que, após a importação, loggingvocê precisa ligar logging.basicConfig()pelo menos uma vez. Caso contrário, você pode se surpreender ao saber que os registradores de crianças não imprimirão nada. As funções de registro no logger root o chamam de maneira preguiçosa.
Hubert Grzeskowiak
58

Muitos anos depois, parece ainda haver um problema de usabilidade com o logger do Python. Aqui estão algumas explicações com exemplos:

import logging
# This sets the root logger to write to stdout (your console).
# Your script/app needs to call this somewhere at least once.
logging.basicConfig()

# By default the root logger is set to WARNING and all loggers you define
# inherit that value. Here we set the root logger to NOTSET. This logging
# level is automatically inherited by all existing and new sub-loggers
# that do not set a less verbose level.
logging.root.setLevel(logging.NOTSET)

# The following line sets the root logger level as well.
# It's equivalent to both previous statements combined:
logging.basicConfig(level=logging.NOTSET)


# You can either share the `logger` object between all your files or the
# name handle (here `my-app`) and call `logging.getLogger` with it.
# The result is the same.
handle = "my-app"
logger1 = logging.getLogger(handle)
logger2 = logging.getLogger(handle)
# logger1 and logger2 point to the same object:
# (logger1 is logger2) == True


# Convenient methods in order of verbosity from highest to lowest
logger.debug("this will get printed")
logger.info("this will get printed")
logger.warning("this will get printed")
logger.error("this will get printed")
logger.critical("this will get printed")


# In large applications where you would like more control over the logging,
# create sub-loggers from your main application logger.
component_logger = logger.getChild("component-a")
component_logger.info("this will get printed with the prefix `my-app.component-a`")

# If you wish to control the logging levels, you can set the level anywhere 
# in the hierarchy:
#
# - root
#   - my-app
#     - component-a
#

# Example for development:
logger.setLevel(logging.DEBUG)

# If that prints too much, enable debug printing only for your component:
component_logger.setLevel(logging.DEBUG)


# For production you rather want:
logger.setLevel(logging.WARNING)

Uma fonte comum de confusão vem de um logger root mal inicializado. Considere isto:

import logging
log = logging.getLogger("myapp")
log.warning("woot")
logging.basicConfig()
log.warning("woot")

Resultado:

woot
WARNING:myapp:woot

Dependendo do seu ambiente de tempo de execução e níveis de registro, a primeira linha de registro (antes da configuração básica) pode não aparecer em qualquer lugar .

Hubert Grzeskowiak
fonte
Meu registro não está funcionando, pois não produz nenhum arquivo de saída. Você vê alguma coisa que estou fazendo que está claramente errada? logging.basicConfig( filename='logging.txt', level=logging.DEBUG) logger = logging.getLogger() logger.info('Test B') logging.info('Test A')
Rylan Schaeffer
O arquivo de registro ainda nem foi criado
Rylan Schaeffer
Percebi que, depois de eliminar um ponto de interrupção logger = logging.getLogger(), o nível está definido como WARNING, embora eu tenha especificado o nível como DEBUG. Você sabe o que estou fazendo de errado?
Rylan Schaeffer
Olá @RylanSchaeffer, talvez você queira criar uma nova pergunta e fornecer mais alguns detalhes. Isso também dará a outras pessoas a chance de ajudá-lo.
Hubert Grzeskowiak
Eu fiz. Frequentemente, fazer um comentário é uma maneira mais rápida de encontrar uma resposta, porque pelo menos uma pessoa com conhecimento verá minha pergunta
Rylan Schaeffer
20

Para qualquer um aqui que deseja uma resposta super simples: basta definir o nível que deseja exibir. No topo de todos os meus scripts, acabei de colocar:

import logging
logging.basicConfig(level = logging.INFO)

Em seguida, para exibir qualquer coisa nesse nível ou acima:

logging.info("Hi you just set your fleeb to level plumbus")

É um conjunto hierárquico de cinco níveis para que os logs sejam exibidos no nível que você definir ou superior . Portanto, se você quiser exibir um erro, pode usar logging.error("The plumbus is broken").

Os níveis, em ordem crescente de severidade, são DEBUG, INFO, WARNING, ERROR, e CRITICAL. A configuração padrão é WARNING.

Este é um bom artigo que contém essas informações expressas melhor do que minha resposta:
https://www.digitalocean.com/community/tutorials/how-to-use-logging-in-python-3

Eric
fonte
14

Talvez tente isso? Parece que o problema foi resolvido após remover todos os manipuladores do meu caso.

for handler in logging.root.handlers[:]:
    logging.root.removeHandler(handler)

logging.basicConfig(filename='output.log', level=logging.INFO)
yue dong
fonte
SyntaxError: invalid syntax
Eric
2
Por que isso é necessário? Quais manipuladores vêm com o logger python e por que eles estão lá para começar? Ou talvez a questão seja: por que o basicConfig não os substitui / substitui?
jrh