Quando desenvolvemos aplicações Python, especialmente em ambiente de produção, a capacidade de registrar eventos, erros e informações de debug é fundamental. O módulo logging do Python é a ferramenta padrão e mais versátil para essa finalidade, oferecendo muito mais do que simples prints no console.
Neste tutorial completo, você aprenderá desde os conceitos básicos até técnicas avançadas de logging em Python, incluindo configuração de handlers, formatters personalizados, filtros e integração com sistemas externos de monitoramento.
O Que é Python Logging?
O logging é o processo de registrar informações sobre a execução de um programa. Diferente do print(), o módulo logging oferece uma estrutura hierárquica, múltiplos níveis de severidade, configuração centralizada e a possibilidade de enviar logs para diferentes destinos simultaneamente.
Com o logging adequado, você pode:
- Monitorar o comportamento da aplicação em produção
- Debugar problemas em ambiente de desenvolvimento
- Auditar ações de usuários eシステム eventos
- Integrar com ferramentas de monitoramento como ELK, Datadog, New Relic
- Gerar métricas de performance e disponibilidade
Fonte: Python Documentation - Logging
Os 5 Níveis de Logging
O Python define cinco níveis de logging, cada um com uma finalidade específica:
import logging
# DEBUG - Informações detalhadas para diagnóstico
logging.debug("Variável x = %d", x)
# INFO - Confirmação de que tudo está funcionando
logging.info("Usuário %s fez login", username)
# WARNING - Algo inesperado aconteceu, mas a aplicação continua
logging.warning("Memória acima de 80%%")
# ERROR - Problema sério que afectó uma função
logging.error("Falha ao conectar com banco de dados")
# CRITICAL - Erro crítico que pode parar a aplicação
logging.critical("Sistema de autenticação falhou!")
Cada nível tem um valor numérico associated: DEBUG=10, INFO=20, WARNING=30, ERROR=40, CRITICAL=50. Por padrão, apenas WARNING e superiores são exibidos.
Fonte: Real Python - Python Logging
Configuração Básica de Logging
A forma mais simples de começar com logging é usando a função basicConfig():
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info("Logging configurado com sucesso!")
Este código cria um logger raiz com output para o console. O formato inclui timestamp, nome do logger, nível e mensagem.
Configuração com dicionário (versão moderna)
A partir do Python 3.21, você pode usar dictionaries para configuração:
import logging
import logging.config
config = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"standard": {
"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "DEBUG",
"formatter": "standard",
"stream": "ext://sys.stdout"
},
"file": {
"class": "logging.FileHandler",
"level": "INFO",
"formatter": "standard",
"filename": "app.log",
"mode": "a"
}
},
"root": {
"level": "INFO",
"handlers": ["console", "file"]
}
}
logging.config.dictConfig(config)
Esta abordagem é muito mais flexível e permite configuração sem alterar código, sendo ideal para ambientes de produção.
Criando Loggers Personalizados
Para aplicações maiores, é recommend criar loggers específicos para cada módulo:
import logging
# Criar logger para um módulo específico
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# Adicionar handler se não existir
if not logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
# Usar o logger
logger.info("Processando dados do usuário")
logger.error("Falha na validação")
Usar __name__ como argumento para getLogger() cria um logger com o nome do módulo atual, o que facilita a identificação da origem dos logs.
Handlers: Destinos dos Logs
Handlers definem para onde os logs serão enviados. O Python oferece vários tipos:
StreamHandler - Console e Arquivos
handler = logging.StreamHandler() # stdout (padrão)
handler = logging.FileHandler('app.log', encoding='utf-8') # arquivo
handler = logging.FileHandler('error.log', mode='a') # append mode
RotatingFileHandler - Logs com Rotação
Para evitar arquivos de log enorms, use rotação:
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
'app.log',
maxBytes=10 * 1024 * 1024, # 10 MB
backupCount=5 # manter 5 arquivos de backup
)
Quando o arquivo atinge 10 MB, ele é renomeado e um novo é criado. O parâmetro backupCount define quantos arquivos antigos são mantidos.
Fonte: Python Logging Handlers
TimedRotatingFileHandler - Rotação por Tempo
from logging.handlers import TimedRotatingFileHandler
handler = TimedRotatingFileHandler(
'app.log',
when='midnight', # rotates at midnight
interval=1,
backupCount=30 # keep 30 days of logs
)
SysLogHandler - Enviar para Sistema
Para enviar logs para servidores syslog:
from logging.handlers import SysLogHandler
handler = SysLogHandler(address=('localhost', 514))
HTTPHandler - Enviar para API
Para enviar logs para serviços externos:
from logging.handlers import HTTPHandler
handler = HTTPHandler(
'api.monitoring.com',
'/logs',
method='POST'
)
Formatters: Formatando a Saída
Formatters definem o layout das mensagens de log. O Python oferece vários atributos:
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
Atributos disponíveis:
%(asctime)s- Timestamp formatado%(name)s- Nome do logger%(levelname)s- Nível do log (DEBUG, INFO, etc)%(levelno)s- Número do nível%(message)s- A mensagem%(filename)s- Nome do arquivo%(funcName)s- Nome da função%(lineno)d- Número da linha%(process)d- ID do processo%(thread)d- ID da thread%(pathname)s- Caminho completo do arquivo
Criando um Custom Formatter
class ColoredFormatter(logging.Formatter):
"""Formatter com cores para console"""
grey = "\x1b[38;21m"
blue = "\x1b[38;5;39m"
yellow = "\x1b[38;5;226m"
red = "\x1b[38;5;196m"
bold_red = "\x1b[31;1m"
reset = "\x1b[0m"
FORMATS = {
logging.DEBUG: grey + "%(message)s" + reset,
logging.INFO: blue + "%(message)s" + reset,
logging.WARNING: yellow + "%(message)s" + reset,
logging.ERROR: red + "%(message)s" + reset,
logging.CRITICAL: bold_red + "%(message)s" + reset
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
Este formatter adiciona cores aos logs no console, facilitando a identificação visual do nível de cada mensagem.
Filtros: Controlando o Fluxo de Logs
Filtros permitem um controle mais granular sobre quais mensagens são registradas:
class SensitiveDataFilter(logging.Filter):
"""Filtro que removes dados sensíveis dos logs"""
SENSITIVE_PATTERNS = [
r'\b\d{3}-\d{2}-\d{4}\b', # CPF
r'\bpassword[=:]\S+', # Password
r'\bapi_key[=:]\S+', # API Key
]
def filter(self, record):
import re
message = record.getMessage()
for pattern in self.SENSITIVE_PATTERNS:
message = re.sub(pattern, '***REDACTED***', message)
record.msg = message
return True
# Adicionar filtro ao handler
handler.addFilter(SensitiveDataFilter())
Este filtro é essencial para compliance com LGPD/GDPR, removendo informações pessoais dos logs.
Filtro por Módulo
class ModuleFilter(logging.Filter):
"""Filtro que permite apenas módulos específicos"""
def __init__(self, allowed_modules):
super().__init__()
self.allowed_modules = allowed_modules
def filter(self, record):
return record.name in self.allowed_modules or record.levelno >= logging.ERROR
Logging em Aplicações Web (Django/Flask)
Configuração para Django
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
'file': {
'class': 'logging.FileHandler',
'filename': 'django.log',
'formatter': 'verbose',
},
},
'root': {
'handlers': ['console', 'file'],
'level': 'INFO',
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'WARNING',
'propagate': False,
},
'myapp': {
'handlers': ['console', 'file'],
'level': 'DEBUG',
'propagate': False,
},
},
}
Configuração para Flask
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
if not app.debug:
file_handler = RotatingFileHandler(
'flask.log',
maxBytes=10240000,
backupCount=10
)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Aplicação Flask iniciada')
Fonte: DigitalOcean - Python Logging
Logging Assíncrono
Para aplicações de alta performance, considere logging assíncrono:
import logging
from logging.handlers import QueueHandler
import queue
import threading
# Criar queue para logs
log_queue = queue.Queue(-1)
# Handler que envia para a queue
queue_handler = QueueHandler(log_queue)
# Thread que processa a queue
def log_writer():
while True:
record = log_queue.get()
if record is None:
break
logger = logging.getLogger(record.name)
logger.handle(record)
# Iniciar thread de escrita
writer_thread = threading.Thread(target=log_writer, daemon=True)
writer_thread.start()
# Configurar logger raiz com QueueHandler
root_logger = logging.getLogger()
root_logger.addHandler(queue_handler)
root_logger.setLevel(logging.DEBUG)
# Agora todo logging é assíncrono
logging.info("Log assíncrono!")
Esta abordagem é especialmente útil em aplicações web de alto tráfego onde o I/O de logging pode se tornar um gargalo.
Boas Práticas de Logging
- Use níveis corretamente: Não use DEBUG em produção, nem ERROR para mensagens informativas.
- Inclua contexto: Adicione IDs de requisição, nomes de usuário, dados relevantes.
- Evite dados sensíveis: Nunca logue senhas, tokens, dados pessoais sem criptografia.
- Seja consistente: Use o mesmo formato em toda a aplicação.
- Configure rotação: Evite logs que consumam todo o espaço em disco.
- Monitore seus logs: Use ferramentas como ELK, Datadog, Sentry.
- Documente sua configuração: altri configuração deve estar documentada.
Exemplo de Logger Estruturado
import logging
import json
from datetime import datetime
class JSONFormatter(logging.Formatter):
"""Formatter que outputa JSON"""
def format(self, record):
log_data = {
"timestamp": datetime.utcnow().isoformat(),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
"module": record.module,
"function": record.funcName,
"line": record.lineno
}
if record.exc_info:
log_data["exception"] = self.formatException(record.exc_info)
return json.dumps(log_data)
Logs em JSON são ideais para processamento por sistemas de monitoring e log aggregation.
Fonte: Loggly - Ultimate Guide to Python Logging
Logging e Exception Handling
Uma prática essencial é usar logging com tratamento de exceções:
import logging
import traceback
logger = logging.getLogger(__name__)
try:
result = process_data(data)
except Exception as e:
logger.error(
"Falha ao processar dados: %s\n%s",
str(e),
traceback.format_exc()
)
raise
# Maneira mais limpa com logging.exception()
try:
result = process_data(data)
except Exception:
logger.exception("Falha ao processar dados")
raise
O método logging.exception() automaticamente inclui o traceback completo.
Integração com Sistemas de Monitoramento
Sentry
import sentry_sdk
from sentry_sdk.integrations.logging import SentryHandler
sentry_sdk.init(
dsn="YOUR_SENTRY_DSN",
integrations=[LoggingIntegration()]
)
handler = SentryHandler()
logging.root.addHandler(handler)
Datadog
from datadog import DogStatsd
statsd = DogStatsd(host="localhost", port=8125)
class DatadogHandler(logging.Handler):
def emit(self, record):
if record.levelno >= logging.ERROR:
statsd.increment("log.error", tags=[
f"logger:{record.name}",
f"level:{record.levelname}"
])
logging.root.addHandler(DatadogHandler())
Conclusão
O módulo logging do Python é uma ferramenta extremamente poderosa que vai muito além do simples print(). Com a configuração correta de handlers, formatters e filtros, você pode criar um sistema de logging profissional adequado para aplicações de qualquer tamanho.
Lembre-se das melhores práticas: use níveis adequados, inclua contexto relevante, evite dados sensíveis e configure rotação de logs. Com um bom sistema de logging, debugging e monitoramento tornam-se muito mais eficientes.
Continue aprendendo com os guias gratuitos do Universo Python: explore tratamento de exceções, testes automatizados com pytest, e criar APIs com FastAPI para elevar suas habilidades de desenvolvimento!