Introdução ao Monitoramento de APIs REST com Python
No cenário atual de infraestrutura moderna, onde microsserviços e arquiteturas distribuídas são a norma, a disponibilidade de APIs REST torna-se crítica para a operação contínua dos negócios. Para profissionais de TI, sysadmins e desenvolvedores, saber monitorar api rest não é apenas uma boa prática, mas uma necessidade operacional. O monitoramento proativo permite identificar falhas antes que elas impactem o usuário final, garantindo a integridade dos dados e a performance do sistema.
O Python se destaca como a linguagem de escolha para essa tarefa devido à sua sintaxe clara, vasta biblioteca padrão e ecossistema robusto de bibliotecas de terceiros. Neste tutorial, vamos construir um script de automação em Python que realiza requisições http periódicas para validar endpoints, verifica o status code retornado e executa validações de conteúdo. Ao final, você terá uma ferramenta pronta para integrar em seus pipelines de CI/CD ou como um serviço systemd em produção.
Não utilizaremos frameworks pesados neste exemplo inicial. O foco é a simplicidade e a eficiência, utilizando a biblioteca requests, que é o padrão da indústria para fazer chamadas HTTP em Python. Este guia cobre desde a instalação das dependências até a criação de um script robusto com tratamento de erros e lógica de verificação.
Pré-requisitos e Instalação do Ambiente
Antes de escrever o código, é fundamental preparar o ambiente de desenvolvimento. Recomendamos o uso de ambientes virtuais para isolar as dependências do projeto e evitar conflitos com pacotes globais do sistema operacional.
- Crie um diretório para o seu projeto de monitoramento:
mkdir api-monitor-python
cd api-monitor-python
- Crie e ative um ambiente virtual Python:
python3 -m venv venv
source venv/bin/activate
- Instale a biblioteca
requests. Ela simplifica enormemente o processo de fazer chamadas HTTP, lidando com conexões persistentes e decodificação JSON automaticamente:
pip install requests
Com o ambiente configurado, estamos prontos para estruturar a lógica do nosso script. A estrutura do código será modular, separando a configuração, a função de chamada e a lógica de validação.
Estruturando o Script de Monitoramento
A primeira etapa da programação em Python para este cenário é definir as configurações básicas. Um script de automação eficaz deve ser configurável sem necessidade de alterar o código-fonte principal. Utilizaremos um dicionário ou variáveis globais para armazenar a URL base, os cabeçalhos (headers) necessários e os tempos de espera.
Crie o arquivo monitor.py e inicie com a importação das bibliotecas necessárias:
import requests
import time
import sys
import json
Agora, vamos definir a estrutura de configuração. Para fins didáticos, utilizaremos variáveis claras que podem ser facilmente substituídas por leitura de arquivos .env ou configurações YAML em um projeto real.
# Configurações do Endpoint
API_URL = "https://jsonplaceholder.typicode.com/posts/1"
TIMEOUT_SECONDS = 5
MAX_RETRIES = 3
EXPECTED_STATUS = 200
A variável API_URL aponta para um serviço de teste público (JSONPlaceholder) que simula uma API REST real. O TIMEOUT_SECONDS define quanto tempo o script deve esperar por uma resposta antes de considerar a conexão como falha, evitando que o monitor fique bloqueado indefinidamente. O MAX_RETRIES permite tentar a conexão novamente em caso de falhas transitórias de rede, enquanto EXPECTED_STATUS define qual código HTTP indica sucesso.
Implementando a Função de Check Health API
O núcleo do nosso script é a função que realiza a requisição. Vamos criar uma função chamada check_endpoint que recebe a URL e os parâmetros de timeout. Esta função deve retornar um dicionário com o resultado da operação, incluindo sucesso ou falha, tempo de resposta e corpo da resposta (se aplicável).
def check_endpoint(url, timeout):
"""
Realiza uma requisição GET para a URL especificada.
Args:
url (str): A URL do endpoint a ser verificado.
timeout (int): Tempo limite em segundos para a resposta.
Returns:
dict: Resultado da operação com status_code, sucesso e mensagem.
"""
result = {
"success": False,
"status_code": None,
"response_time": 0,
"message": ""
}
try:
# Inicia o timer para medir a latência
start_time = time.time()
# Realiza a requisição HTTP GET
response = requests.get(url, timeout=timeout)
# Calcula o tempo de resposta em segundos
result["response_time"] = round(time.time() - start_time, 3)
# Armazena o status code retornado
result["status_code"] = response.status_code
# Verifica se a requisição foi bem-sucedida (código 2xx)
if response.status_code == 200:
result["success"] = True
result["message"] = "Endpoint responded with 200 OK"
else:
result["success"] = False
result["message"] = f"Unexpected status code: {response.status_code}"
except requests.exceptions.Timeout:
result["message"] = "Request timed out"
except requests.exceptions.ConnectionError:
result["message"] = "Connection error (host unreachable)"
except Exception as e:
result["message"] = f"Unexpected error: {str(e)}"
return result
Note a utilização do bloco try-except. Em scripts de automação, o tratamento de exceções é vital. Se a API estiver fora do ar ou se houver um problema de DNS, o script não deve crashar; ele deve registrar o erro e continuar operando ou alertar o administrador. As exceções capturadas aqui cobrem os cenários mais comuns de falha em requisições http.
Adicionando Validação de Endpoints e Conteúdo
Saber que a API respondeu com 200 OK é importante, mas nem sempre suficiente. Em cenários reais, uma API pode retornar sucesso técnico, mas entregar dados corrompidos ou incompletos. Para isso, precisamos implementar validações de conteúdo dentro da nossa lógica de validação endpoints.
Vamos modificar a função anterior para verificar se o corpo da resposta contém um campo esperado. No caso do nosso exemplo (JSONPlaceholder), esperamos que o objeto retornado contenha a chave "title".
def validate_endpoint_content(response_data, expected_keys):
"""
Valida se os dados esperados estão presentes na resposta JSON.
Args:
response_data (dict): Dados JSON decodificados da resposta.
expected_keys (list): Lista de chaves que devem existir no JSON.
Returns:
bool: True se todas as chaves existirem, False caso contrário.
"""
if not isinstance(response_data, dict):
return False
for key in expected_keys:
if key not in response_data:
return False
return True
Agora, integramos essa validação à função principal. Após obter o status 200, tentaremos decodificar o JSON e verificar as chaves.
def check_endpoint_advanced(url, timeout, expected_keys=None):
result = {
"success": False,
"status_code": None,
"response_time": 0,
"message": "",
"content_valid": False
}
try:
start_time = time.time()
response = requests.get(url, timeout=timeout)
result["response_time"] = round(time.time() - start_time, 3)
result["status_code"] = response.status_code
if response.status_code == 200:
try:
data = response.json()
# Se houver validação de conteúdo configurada
if expected_keys:
result["content_valid"] = validate_endpoint_content(data, expected_keys)
if not result["content_valid"]:
result["message"] = "Content validation failed: missing keys"
else:
result["success"] = True
result["message"] = "Health check passed with content validation"
else:
# Apenas verificação de status code
result["success"] = True
result["message"] = "Health check passed (status 200 only)"
except ValueError:
result["message"] = "Response is not valid JSON"
else:
result["success"] = False
result["message"] = f"Status code {response.status_code} indicates failure"
except requests.exceptions.Timeout:
result["message"] = "Request timed out"
except requests.exceptions.ConnectionError:
result["message"] = "Connection error"
except Exception as e:
result["message"] = f"Unexpected error: {str(e)}"
return result
Essa abordagem permite que você estenda a lógica para verificar códigos de erro específicos, tamanhos de payload ou até mesmo assinaturas digitais em headers, dependendo das necessidades do seu check health api.
Automatizando o Loop e Gerenciamento de Retries
Um script de monitoramento raramente é executado apenas uma vez. Ele precisa rodar continuamente ou ser acionado por um agendador (como cron). Vamos criar a função principal que gerencia o loop de execução e as tentativas de reconexão.
def run_monitor(url, timeout=5, retries=3, delay_between_checks=60, expected_keys=None):
"""
Loop principal do monitoramento.
Args:
url (str): URL a monitorar.
timeout (int): Timeout da requisição.
retries (int): Número máximo de tentativas em caso de falha instantânea.
delay_between_checks (int): Segundos entre cada verificação.
expected_keys (list): Chaves para validação de conteúdo.
"""
print(f"[*] Iniciando monitoramento de: {url}")
while True:
attempt = 0
# Loop de tentativas internas antes de esperar o próximo ciclo
while attempt < retries:
attempt += 1
print(f"[{time.strftime('%H:%M:%S')}] Tentativa {attempt}/{retries}...")
result = check_endpoint_advanced(url, timeout, expected_keys)
if result["success"]:
print(f" [SUCESSO] Status: {result['status_code']} | Tempo: {result['response_time']}s | Msg: {result['message']}")
break # Sai do loop de tentativas se tiver sucesso
else:
print(f" [FALHA] {result['message']}")
# Se todas as tentativas falharam, espera o intervalo definido
if not result["success"]:
print(f"[!] Todas as {retries} tentativas falharam. Aguardando {delay_between_checks}s para próxima verificação...")
time.sleep(delay_between_checks)
else:
# Se teve sucesso, aguarda o intervalo padrão antes da próxima rodada
time.sleep(delay_between_checks)
A estrutura acima implementa uma lógica de "circuit breaker" simplificada. Se a API estiver caindo, o script tentará várias vezes rapidamente (para confirmar que não é um spike momentâneo) e depois esperará mais tempo entre as verificações para não sobrecarregar a rede ou ser bloqueado por rate limits.
Executando o Script de Automação
Para executar o script, precisamos chamar a função principal dentro do bloco condicional padrão do Python. Adicione o seguinte código ao final do arquivo monitor.py:
if __name__ == "__main__":
# Definição dos parâmetros de execução
TARGET_URL = "https://jsonplaceholder.typicode.com/posts/1"
# Chaves esperadas no JSON para validação avançada
EXPECTED_FIELDS = ["id", "title", "body"]
try:
run_monitor(
url=TARGET_URL,
timeout=5,
retries=3,
delay_between_checks=10, # Para teste rápido, use 10s. Em produção, ajuste para 60s ou mais.
expected_keys=EXPECTED_FIELDS
)
except KeyboardInterrupt:
print("\n[*] Monitoramento interrompido pelo usuário.")
sys.exit(0)
Agora, execute o script no terminal:
python monitor.py
Você verá a saída no console mostrando o timestamp de cada verificação, o status code retornado e o tempo de latência. Se a API estiver saudável, você verá mensagens de sucesso repetidamente.
Bonificação: Integrando com Sistemas de Alerta
Em um ambiente profissional, apenas logar no console não é suficiente. É necessário integrar com ferramentas como Slack, Telegram, ou sistemas de monitoramento como Prometheus/Grafana ou Zabbix. Para isso, a estrutura de dicionário retornada pela função check_endpoint_advanced é perfeita.
Você pode facilmente adicionar uma função que converte o resultado em JSON e envia para um webhook:
def send_alert(result):
"""Exemplo simplificado de envio de alerta"""
if not result["success"]:
payload = {
"text": f"Alerta: Falha no endpoint! Status: {result['status_code']}. Mensagem: {result['message']}"
}
# requests.post("https://hooks.slack.com/...", json=payload)
print(f"[ALERTA] Enviando alerta para canal de monitoramento: {payload['text']}")
Basta chamar send_alert(result) dentro do loop principal quando o status for negativo. Isso transforma seu script simples em uma ferramenta completa de observabilidade.
Considerações Finais sobre Scripts de Automação
A criação de scripts de automação em Python para monitoramento api rest oferece flexibilidade e controle que soluções "black box" não proporcionam. Você decide exatamente o que validar: apenas a conectividade, o status HTTP, a integridade do JSON ou até mesmo regras de negócio complexas.
Lembre-se de ajustar os tempos de timeout e retry conforme a criticidade do serviço. APIs externas podem ter latências variáveis, enquanto APIs internas devem ser extremamente rápidas. Além disso, sempre utilize ambientes virtuais e versionamento de código (Git) para manter o controle das suas alterações nos scripts automação.
Com esta base, você está preparado para expandir suas capacidades de DevOps, criando dashboards personalizados ou integrando esse script em orquestradores como Kubernetes ou Ansible. O domínio de programação python para infraestrutura é um diferencial competitivo essencial para qualquer equipe de TI moderna.