last modified July 6, 2020
Tutorial de registo Python mostra como fazer o registo em Python com o loggingmodule.
Logging
Logging é o processo de escrever informação em ficheiros de registo. Os ficheiros de registo contêm informação sobre vários eventos que ocorreram no sistema operativo, software, ou na comunicação.
Fim de registo
O registo é feito para os seguintes fins:
- Recolha de informação
- Relaboração de problemas
- Geração de estatísticas
- Auditoria
- Perfil
O registo não se limita à identificação de erros no desenvolvimento de software. É também utilizado para detectar incidentes de segurança, monitorizar violações de políticas, fornecer informação em caso de problemas, encontrar estrangulamentos na aplicação, ou gerar dados de utilização.
Que eventos a registar
Eventos que devem ser registados incluem falhas de validação de entrada, autenticação e falhas de autorização, erros de aplicação, alterações de configuração, e arranque e encerramento de aplicações.
Que eventos não a registar
Eventos que não devem ser registados incluem código fonte da aplicação, valores de identificação da sessão, fichas de acesso, dados pessoais sensíveis, palavras-passe, cadeias de ligação à base de dados, chaves de encriptação, conta bancária e dados do titular do cartão.
Melhores práticas de registo
As seguintes são algumas das melhores práticas para efectuar o registo:
- O registo deve ser significativo.
- Ologging deve conter contexto.
- Ologging deve ser estruturado e feito a diferentes níveis.
- Ologging deve ser equilibrado; não deve incluir muito pouca ou muita informação.
- As mensagens de log devem ser compreensíveis para os humanos e analisáveis por máquinas.
- Ologging em aplicações mais complexas deve ser feito em vários ficheiros de log.
- O registo deve ser adaptado ao desenvolvimento e à produção.
O módulo de registo
Módulo de registo python define funções e classes que implementam um sistema flexível de registo de eventos para aplicações e bibliotecas.
Os componentes do módulo de registo
O módulo de registo tem quatro componentes principais: registadores, manipuladores, filtros, e formatadores. Os manipuladores enviam os registos de registo (criados pelos registadores) para o destino apropriado. Os filtros fornecem uma facilidade mais fina para determinar quais os registos de registo a produzir. Os formatadores especificam a disposição dos registos de registo na produção final.
Hierarquia de registo de pitões
Registradores de pitões formam uma hierarquia. Um logger chamado main
é um parentof main.new
.
Leitores de registo de crianças propagam mensagens até aos manipuladores associados com os seus antepassados. Devido a isto, é desnecessário definir e configurar os manipuladores para todos os registadores na aplicação. É suficiente configurar manipuladores para um registador de nível superior e criar registadores de crianças conforme necessário.
Níveis de registo de pitões
Níveis são utilizados para identificar a gravidade de um evento. Existem seis níveis de registo:
- CRÍTICO
- ERROR
- AVISO
- DEBUG
- NOTSET
INFO
Se o nível de registo estiver definido para WARNING
, all WARNING
ERROR
, e CRITICAL
as mensagens são escritas no ficheiro de registo ou na consola. Se estiver definido para ERROR
, apenas ERROR
e CRITICAL
as mensagens são registadas.
Os registadores têm um conceito de nível efectivo. Se um nível não é explicitamente fixado num logger, o nível do seu pai é utilizado em vez disso como nível itseffective. Se o pai não tiver um nível definido explicitamente, o seu pai é examinado, e assim por diante – todos os antepassados são pesquisados até ser encontrado um nível definido explicitamente.
Quando o logger é criado com getLogger()
, o nível é definido paraNOTSET
. Se o nível de registo não for definido explicitamente com setLevel()
,as mensagens são propagadas aos pais dos registadores. A cadeia de loggers de antepassados do logger atravessou até ser encontrado ou um antepassado com um nível diferente de NOTSET
,ou a raiz é atingida. O logger de raiz tem um padrão WARNING
nível definido.
Root logger
Todos os loggers são descendentes do logger de raiz. Cada logger passa mensagens de logon ao seu pai. Novos loggers são criados com o getLogger(name)
method. Chamando a função sem nome (getLogger()
) retorna o root logger.
O root logger tem sempre um nível explícito definido, que é WARNING
por defeito.
O root looger senta-se no topo da hierarquia e está sempre presente, mesmo que não esteja configurado. Em geral, o programa ou biblioteca não deve registar directamente contra o root logger. Em vez disso, um logger específico para o programa deve ser configurado. O logger de raiz pode ser utilizado para ligar e desligar facilmente todos os loggers de todas as bibliotecas.
Python logging exemplo simples
O módulo logging
tem métodos simples que podem ser utilizados de imediato sem qualquer configuração. Isto pode ser usado para registo simples.
#!/usr/bin/env pythonimport logginglogging.debug('This is a debug message')logging.info('This is an info message')logging.warning('This is a warning message')logging.error('This is an error message')logging.critical('This is a critical message')
O exemplo chama cinco métodos do módulo logging
.
$ simple.pyWARNING:root:This is a warning messageERROR:root:This is an error messageCRITICAL:root:This is a critical message
Notificação de que o root logger é utilizado e apenas três mensagens foram escritas.Isto porque por defeito, apenas mensagens com nível de aviso e up são escritas.
Python define o nível de registo
O nível de registo é definido com setLevel()
.Define o limiar para este registador com lvl
.As mensagens de registo que são menos severas que lvl
serão ignoradas.
#!/usr/bin/env pythonimport logginglogger = logging.getLogger('dev')logger.setLevel(logging.DEBUG)logger.debug('This is a debug message')logger.info('This is an info message')logger.warning('This is a warning message')logger.error('This is an error message')logger.critical('This is a critical message')
No exemplo, alteramos o nível de registo para DEBUG
.
logger = logging.getLogger('dev')
O getLogger()
devolve um logger com o nome especificado. Se o nome for None
, devolve o logger de raiz. O nome pode ser uma cadeia de pontos separada que define a hierarquia de registo; por exemplo’a’, ‘a.b’, ou ‘a.b.c’. Note-se que existe um nome de raiz implícito, que não é mostrado.
$ set_level.pyThis is a warning messageThis is an error messageThis is a critical message
Agora todas as mensagens foram escritas.
Nível de registo efectivo de pitão
O nível de registo efectivo é o nível estabelecido explicitamente determinado pelos pais do registador.
#!/usr/bin/env pythonimport loggingmain_logger = logging.getLogger('main')main_logger.setLevel(5)dev_logger = logging.getLogger('main.dev')print(main_logger.getEffectiveLevel())print(dev_logger.getEffectiveLevel())
No exemplo, examinamos o nível efectivo de exploração madeireira de dois loggers.
dev_logger = logging.getLogger('main.dev')
O nível do dev_logger
não está definido; o nível do seu pai é então utilizado.
$ effective_level.py55
Esta é a saída.
Python logging handlers
Handler é um objecto responsável pelo envio das mensagens de registo apropriadas (com base na gravidade das mensagens de registo) para o destino especificado pelo handler.
Handlers são propagados como níveis. Se o logger tiver nohandler definido, a sua cadeia de antepassados procura um manipulador.
#!/usr/bin/env pythonimport logginglogger = logging.getLogger('dev')logger.setLevel(logging.INFO)fileHandler = logging.FileHandler('test.log')fileHandler.setLevel(logging.INFO)consoleHandler = logging.StreamHandler()consoleHandler.setLevel(logging.INFO)logger.addHandler(fileHandler)logger.addHandler(consoleHandler)logger.info('information message')
O exemplo cria dois manipuladores para um logger: um manipulador de ficheiros e um manipulador de consola.
fileHandler = logging.FileHandler('test.log')
FileHandler
envia registos de registo para o ficheiro test.log
.
consoleHandler = logging.StreamHandler()
StreamHandler
envia registos de registo para um fluxo. Se o fluxo não for especificado, é utilizado o sys.stderr
.
logger.addHandler(fileHandler)
O manipulador é adicionado ao registador com addHandler()
.
Formatores de registo de píton
Formatéria é um objecto que configura a ordem final, estrutura, e conteúdo do registo de registo. Além da cadeia de mensagens, os registos de registo também incluem data e hora,nomes de registo, e gravidade do nível de registo.
#!/usr/bin/env pythonimport logginglogger = logging.getLogger('dev')logger.setLevel(logging.INFO)consoleHandler = logging.StreamHandler()consoleHandler.setLevel(logging.INFO)logger.addHandler(consoleHandler)formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s: %(message)s')consoleHandler.setFormatter(formatter)logger.info('information message')
O exemplo cria um registador de consola e adiciona um formatador ao itshandler.
formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s: %(message)s')
O formatador é criado. Inclui a hora da data, nome do registador, nome do nível de registo, e mensagem de registo.
consoleHandler.setFormatter(formatter)
O formatador é definido para o manipulador com setFormatter()
.
$ formatter.py2019-03-28 14:53:27,446 dev INFO: information message
A mensagem com o formato definido é mostrada na consola.
Python logging basicConfig
The basicConfig()
configura o root logger. Faz a configuração básica para o sistema de registo criando um manipulador de fluxo com um formato padrão. O debug()
info()
warning()
error()
e critical()
callbasicConfig()
automaticamente se não forem definidos manipuladores para o root logger.
#!/usr/bin/env pythonimport logginglogging.basicConfig(filename='test.log', format='%(filename)s: %(message)s', level=logging.DEBUG)logging.debug('This is a debug message')logging.info('This is an info message')logging.warning('This is a warning message')logging.error('This is an error message')logging.critical('This is a critical message')
O exemplo configura o root logger com .
logging.basicConfig(filename='test.log', format='%(filename)s: %(message)s', level=logging.DEBUG)
Com filename
, definimos o ficheiro para o qual escrevemos as mensagens de registo. O format
determina o que está registado no ficheiro; temos o nome do ficheiro e a mensagem. Com level
, definimos o limiar de registo.
$ basic_config.py$ cat test.logbasic_config.py: This is a debug messagebasic_config.py: This is an info messagebasic_config.py: This is a warning messagebasic_config.py: This is an error messagebasic_config.py: This is a critical message
Após executar o programa, temos cinco mensagens escritas no ficheirotest.log
.
ficheiro de registo de PythonConfig
The fileConfig()
lê a configuração de registo do ficheiro em formato configparser.
keys=root,devkeys=consoleHandlerkeys=extend,simplelevel=INFOhandlers=consoleHandlerlevel=INFOhandlers=consoleHandlerqualname=devpropagate=0class=StreamHandlerlevel=INFOformatter=extendargs=(sys.stdout,)format=%(asctime)s - %(name)s - %(levelname)s - %(message)sformat=%(asctime)s - %(message)s
The log.conf
define um registador, manipulador, e formatador.
#!/usr/bin/env pythonimport loggingimport logging.configlogging.config.fileConfig(fname='log.conf')logger = logging.getLogger('dev')logger.info('This is an information message')
O exemplo lê o ficheiro de configuração de registo a partir de log.conf
.
$ file_config.py2019-03-28 15:26:31,137 - dev - INFO - This is an information message
Esta é a saída.
Variável de registo Python
Dados dinâmicos são registados usando formatação de string.
#!/usr/bin/env pythonimport loggingroot = logging.getLogger()root.setLevel(logging.INFO)log_format = '%(asctime)s %(filename)s: %(message)s'logging.basicConfig(filename="test.log", format=log_format)# incident happenserror_message = 'authentication failed'root.error(f'error: {error_message}')
O exemplo escreve dados personalizados para a mensagem de registo.
2019-03-21 14:17:23,196 log_variable.py: error: authentication failed
Esta é a mensagem de registo.
Python log format datetime
A datetime está incluída na mensagem de registo com o asctime
log record. Com a opção de configuração datefmt
, podemos formatar a string datetime.
#!/usr/bin/env pythonimport logginglogger = logging.getLogger()logger.setLevel(logging.DEBUG)log_format = '%(asctime)s %(filename)s: %(message)s'logging.basicConfig(filename="test.log", format=log_format, datefmt='%Y-%m-%d %H:%M:%S')logger.info("information message")
O exemplo formata a data/hora da mensagem de registo.
log_format = '%(asctime)s %(filename)s: %(message)s'
Incluímos a string de data/hora no registo com asctime
.
logging.basicConfig(filename="test.log", format=log_format, datefmt='%Y-%m-%d %H:%M:%S')
O datefmt
formata a sequência de data/hora.
2019-03-21 14:17:23,196 log_variable.py: error: authentication failed2019-03-21 14:23:33 date_time.py: information message
Nota a diferença no formato da sequência de data/hora.
Python logging stack trace
O stack trace é uma pilha de chamadas de funções que foram executadas até ao ponto de um lançamento de excepções. O stack trace está incluído com o exc_info
option.
#!/usr/bin/env pythonimport logginglog_format = '%(asctime)s %(filename)s: %(message)s'logging.basicConfig(filename="test.log", format=log_format)vals = try: print(vals)except Exception as e: logging.error("exception occurred", exc_info=True)
No exemplo, registamos a excepção que é lançada quando molhada para aceder a um índice de lista inexistente.
logging.error("exception occurred", exc_info=True)
O traço da pilha é incluído no registo definindo o exc_info
para True
.
2019-03-21 14:56:21,313 stack_trace.py: exception occurredTraceback (most recent call last): File "C:\Users\Jano\Documents\pyprogs\pylog\stack_trace.py", line 11, in <module> print(vals)IndexError: list index out of range
O rastreamento da pilha é incluído no log.
Registo de Python getLogger
O getLogger()
devolve um logger com o nome especificado. É uma prática comum colocar lá o nome do módulo com __name__
.
Todas as chamadas para esta função com um nome dado retornam a mesma instância de logger. Isto significa que as instâncias de logger nunca precisam de ser passadas entre diferentes partes de uma aplicação.
#!/usr/bin/env pythonimport loggingimport sysmain = logging.getLogger('main')main.setLevel(logging.DEBUG)handler = logging.FileHandler('my.log')format = logging.Formatter('%(asctime)s %(name)s %(levelname)s: %(message)s')handler.setFormatter(format)main.addHandler(handler)main.info('info message')main.critical('critical message')main.debug('debug message')main.warning('warning message')main.error('error message')
O exemplo cria um novo logger com getLogger()
.É-lhe dado um manipulador de ficheiros e um formatador.
main = logging.getLogger('main')main.setLevel(logging.DEBUG)
É criado um registador chamado main
; definimos o nível de registo para DEBUG
.
handler = logging.FileHandler('my.log')
É criado um manipulador de ficheiros. As mensagens serão escritas no ficheiromy.log
file.
format = logging.Formatter('%(asctime)s %(name)s %(levelname)s: %(message)s')handler.setFormatter(format)
É criado um formatador. Inclui a hora, o nome do registador,o nível de registo, e a mensagem para registar. O formatador é colocado no manipulador com setFormatter()
.
main.addHandler(handler)
O manipulador é adicionado ao registador com addHandler()
.
$ cat my.log2019-03-21 14:15:45,439 main INFO: info message2019-03-21 14:15:45,439 main CRITICAL: critical message2019-03-21 14:15:45,439 main DEBUG: debug message2019-03-21 14:15:45,439 main WARNING: warning message2019-03-21 14:15:45,439 main ERROR: error message
Estas são as mensagens de registo escritas.
Configuração YAML de registo python
Detalhes de registo podem ser definidos num ficheiro de configuração YAML.YAML é uma linguagem de serialização de dados legível por humanos. É normalmente utilizado para ficheiros de configuração.
$ pip install pyyaml
Precisamos de instalar pyyaml
module.
version: 1formatters: simple: format: "%(asctime)s %(name)s: %(message)s" extended: format: "%(asctime)s %(name)s %(levelname)s: %(message)s"handlers: console: class: logging.StreamHandler level: INFO formatter: simple file_handler: class: logging.FileHandler level: INFO filename: test.log formatter: extended propagate: falseloggers: dev: handlers: test: handlers: root: handlers:
No ficheiro de configuração, definimos vários formatadores, manipuladores, andloggers. A opção propagate
previne a propagação de mensagens de registo para os registadores pais; no nosso caso, para o registador raiz. Caso contrário, as mensagens seriam duplicadas.
#!/usr/bin/env pythonimport loggingimport logging.configimport yamlwith open('config.yaml', 'r') as f: log_cfg = yaml.safe_load(f.read())logging.config.dictConfig(log_cfg)logger = logging.getLogger('dev')logger.setLevel(logging.INFO)logger.info('This is an info message')logger.error('This is an error message')
No exemplo, lemos o ficheiro de configuração e usamos o dev
logger.
$ log_yaml.py2019-03-28 11:36:54,854 dev: This is an info message2019-03-28 11:36:54,855 dev: This is an error message
Quando executamos o programa, há duas mensagens na consola.Os manipuladores da consola utilizam o formatador simples com menos informação.
...2019-03-28 11:36:54,854 dev INFO: This is an info message2019-03-28 11:36:54,855 dev ERROR: This is an error message
Há mensagens de registo dentro do ficheiro test.log
.São produzidas pelo formatador alargado com mais informação.
Neste tutorial, trabalhámos com a biblioteca de registo Python.
Lista todos os tutoriais Python.