Nunca mais prints! Digam “hello” ao logging em Python
Chega de encher seu código de print que não acrescentam utilidade alguma!
Você está cansado de encher seu código de print
e ficar horas esperando seu código rodar para ver uma mensagem na tela? Olhar fixo para não perder a mensagem… se espirrar, já era, horas desperdiçadas e vamos rodar o código novamente!
Print vs logging
Porque estudar mais uma biblioteca se já existe função
Bom, de fato print e log têm funções similares, mas os arquivos de log são muito mais amplos, versáteis e úteis, principalmente para desenvolvedores. Com eles conseguimos registrar uma gama de informações muito maiores, de forma mais rápida e intuitiva, melhor do que encher seu código de print
que não acrescentam funcionalidade alguma. Sem contar que ao fim de uma execução os arquivos ficam salvos, podendo ser consultados posteriormente, se necessário.
Mas afinal, o que é um arquivo de log?
Arquivos de log são gerados para armazenar/registrar eventos que acontecem. Simples assim! Existe uma biblioteca nativa da linguagem Python chamada logging que facilita esse caminho, ao invés de ter todo o trabalho de criar um arquivo e ir acrescentando informações nele. É um atalho para facilitar nossa vida… pois eu poderia muito bem manipular um arquivo para gravar todas essas informações
Amo tudo que facilita minha vida!
O que armazenar em um arquivo de log?
O que quisermos! É um arquivo de texto comum, como qualquer outro! Simples e direto. Óbvio que para tudo na vida existem as famosas boas práticas. Aqui vão algumas sugestões do que é possível armazenar em um arquivo de log (tudo vai depender da sua necessidade). Vamos à elas:
- Data/hora (timestamp): armazenamos a data e hora específica em que uma informação foi gravada no arquivo de log. Podemos controlar quando ocorreram as execuções de um sistema, por exemplo.
- Nome do arquivo que gerou o log
- Nome da função
- Caminho de um arquivo
- Linha do código que gerou o log
- Mensagens padronizadas (sucesso/falha de execução)
- Informações úteis para usuários/desenvolvedores
- E muitas outras…
Níveis (level) de log
É uma boa prática seguir as orientações da própria documentação oficial da da biblioteca logging. Nela são definidos os seguintes níveis para se armazenar um log, onde cada level possui um número correspondente associado*:
DEBUG (10): informações detalhadas, normalmente de interesse apenas ao diagnosticar problemas.
INFO (20): Confirmação de que tudo está funcionando conforme o esperado.
WARNING (30): Uma indicação de que algo inesperado aconteceu, ou indicativo de algum problema em um futuro próximo (por exemplo, ‘espaço em disco insuficiente’). O software ainda está funcionando conforme o esperado.
CRITICAL (40): Devido a um problema mais sério, o software não conseguiu executar algumas funções.
ERROR (50): Um erro grave, indicando que o próprio programa pode não conseguir continuar em execução.
*Tradução literal da documentação oficial
E como eu faço? Quero criar logo meus arquivos de log (perdão pelo trocadilho…)
Primeiro passo é importar a biblioteca. Não é necessário instalá-la, visto que é nativa da linguagem Python.
import logging
Antes de avançarmos com logs, quero trazer um exemplo onde usamos o print para avaliarmos as saídas de um código simples:
Quando executamos esse trecho de código, veremos na tela (na tela/terminal! Já já veremos como armazenar em um arquivo de log):
Soma: 10 + 50 = 60
Sub: 10 - 50 = -40
Mult: 10 * 50 = -40
Div: 10 / 50 = -40
Pois bem. E onde entra o tal do log?
Para trocarmos de print para log, primeiro importamos a biblioteca com import logging
e trocar print
por logging.warning
. Já me explico sobre o warning
Na tela/terminal, veremos o seguinte resultado:
WARNING:root:Soma: 10 + 50 = 60
WARNING:root:Sub: 10 - 50 = -40
WARNING:root:Mult: 10 * 50 = -40
WARNING:root:Div: 10 / 50 = -40
Bom, já ouve alguma alteração. Concordam? Mas qual a diferença, além do WARNING:root:
? Até aqui nenhuma, por enquanto. Mas porque usamos o logging.warning
?
Por padrão, o Python define que ele só irá armazenar/imprimir/gravar os arquivos com level maior que 30, lembrando da seguinte ordem:
debug (10) < info (20) < warning (30) < critical (40) < error (50)
Ou seja, nenhum log do level info ou debug será armazenado/impresso por padrão! E como alteramos essa configuração?
Basta adicionarmos a seguinte linha de código logo (explico em seguida) abaixo do import da biblioteca:
import logging
logging.basicConfig(filename = "teste.log", level = logging.DEBUG)
- filename: Esse parâmetro define o nome de um arquivo que será gerado durante a execução, ao invés de imprimir na tela. Basta ver que ao executar novamente, nada será impresso, e o arquivo
teste.log
será criado. - level: já este parâmetro define qual o nível de log mínimo para ser armazenado. Nesse caso, como definimos
DEBUG
, todos os outros logs serão armazenados. Caso tenhamos definidoCRITICAL
, somente logs do levelCRITICAL
eERROR
serão gravados no arquivo.
Existem outros parâmetros de configuração que você pode explorá-los aqui, nesta documentação oficial.
Você pode definir, por exemplo, o modo de escrita no arquivo com o parâmetro filemode
(cheque as possíveis opções aqui, mas se você já manipulou arquivos em Python, então conhece w
, r
, a
, x
, etc.). Por padrão o valor é a
de append, ou seja, se não definirmos tal parâmetro, ele vai adicionar os logs ao arquivo já existente. Experimente rodar o código acima mais de uma vez e abrir o arquivo de logs… =)
Também é possível armazenar mensagens personalizadas com o parâmetro format
, que exploraremos em seguida.
Personalizando seus logs
Se quisermos personalizar os logs, podemos acrescentar alguns padrões no parâmetro format
das configurações nologging.basicConfig()
.
Para definirmos um padrão, podemos escolher uma das opções encontradas neste link. Aqui basta copiar a coluna Format dentro do parâmetro format
conforme exemplo abaixo:
logging.basicConfig(
filename = "teste.log"
level = logging.DEBUG,
format = "%(asctime)s :: %(levelno)s :: %(lineno)d")
Quando acrescentamos esse parâmetro, observem o que acontece com o arquivo de log. Aqui ele me gerou o seguinte resultado:
2021-02-08 18:59:14,784 :: 10 :: 262021-02-08 18:59:14,785 :: 10 :: 292021-02-08 18:59:14,785 :: 10 :: 322021-02-08 18:59:14,785 :: 10 :: 35
Eu acrescentei a data e hora da execução do script, o número relacionado ao level (degug = 10, info = 20, etc.) e adicionou a linha do código em que ele executou o log.
Quando você executar, provavelmente verá resultados diferentes, mas de acordo com as configurações que você escolheu. Percebam que as 3 opções %(asctime)s
, %(levelno)s
e %(lineno)d
foram escolhidas e definidas por mim, usando como separador o símbolo ::
.
Esse tipo de configuração facilita muito quando você precisa armazenar qual usuário executou, quando, como, qual linha, qual arquivo, se deu erro ou sucesso. E sem precisar ficar horas na tela esperando seu print. Pode colocar o código pra rodar, tomar um café e ver seu arquivo de log posteriormente!
Em resumo, logs são ótimas alternativas (diria até que infalíveis) para substituir os prints da vida!