feat(monitorizacao): scripts de saúde e telemetria do Gemini CLI

This commit is contained in:
2026-10-28 16:01:09 +00:00
parent ca9a07b1d9
commit f063a2ec8c
3 changed files with 120 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
# Gemini CLI Health Check - Integração com Observabilidade
# Este script verifica o estado dos servidores MCP e regista métricas.
LOG_FILE="/media/ealmeida/Dados/Hub/05-Projectos/Observabilidade/logs/gemini/health.log"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] A iniciar verificação de saúde do Gemini CLI..." >> "$LOG_FILE"
# Verifica se o gemini está no PATH e responde
if ! command -v gemini &> /dev/null; then
echo "[$TIMESTAMP] ERRO: Gemini CLI não encontrado no PATH." >> "$LOG_FILE"
exit 1
fi
# Lista servidores MCP e extrai estado
MCP_STATUS=$(gemini mcp list)
# Procura por servidores desconectados (marcados com ✗ no gemini mcp list)
if echo "$MCP_STATUS" | grep -q "✗"; then
echo "[$TIMESTAMP] ALERTA: Um ou mais servidores MCP estão desconectados!" >> "$LOG_FILE"
echo "$MCP_STATUS" | grep "✗" >> "$LOG_FILE"
else
echo "[$TIMESTAMP] SUCESSO: Todos os servidores MCP estão conectados." >> "$LOG_FILE"
fi
echo "------------------------------------------------" >> "$LOG_FILE"
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env python3
import sys
import json
import os
import time
def pre_tool_hook():
try:
# Lê o input do Gemini
hook_input = json.load(sys.stdin)
session_id = hook_input.get("session_id", "unknown")
# Cria um ficheiro temporário para guardar o tempo de início desta sessão
# Nota: O Gemini CLI executa ferramentas de forma sequencial ou paralela,
# mas os hooks AfterTool recebem o contexto necessário.
temp_dir = os.path.expanduser("~/.gemini/tmp/telemetry")
os.makedirs(temp_dir, exist_ok=True)
# Guardamos o tempo de início num ficheiro nomeado pela sessão
# Para maior precisão em chamadas paralelas, o ideal seria um tool_call_id,
# mas o session_id é a base comum.
with open(os.path.join(temp_dir, f"{session_id}.start"), "w") as f:
f.write(str(time.time()))
print(json.dumps({}))
except Exception:
print(json.dumps({}))
if __name__ == "__main__":
pre_tool_hook()
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
import sys
import json
import os
from datetime import datetime
def log_telemetry():
try:
# Lê o JSON do stdin enviado pelo Gemini CLI
hook_input = json.load(sys.stdin)
# Extrai dados básicos
session_id = hook_input.get("session_id", "unknown")
tool_name = hook_input.get("tool_name", "unknown")
timestamp = hook_input.get("timestamp", datetime.now().isoformat())
# Determina o sucesso/erro a partir da resposta da ferramenta
tool_response = hook_input.get("tool_response", {})
error = tool_response.get("error", "")
# No formato do Gemini, o sucesso pode ser inferido pela ausência de erro
# ou por flags específicas em certas ferramentas.
success = "true" if not error else "false"
# Prepara o log no formato compatível com o Claude (mas com engine: gemini)
# O ficheiro de destino é o mesmo usado pelo Claude para métricas unificadas
log_path = os.path.expanduser("~/.claude-work/tool-invocations.jsonl")
# Tenta calcular a duração
duration = 0
start_file = os.path.expanduser(f"~/.gemini/tmp/telemetry/{session_id}.start")
if os.path.exists(start_file):
with open(start_file, "r") as f:
start_time = float(f.read())
duration = round(time.time() - start_time, 2)
log_entry = {
"ts": timestamp,
"session": session_id,
"tool_name": tool_name,
"success": success == "true",
"error": error,
"duration": duration, # Nova métrica de tempo
"engine": "gemini"
}
# Garante que a pasta existe
os.makedirs(os.path.dirname(log_path), exist_ok=True)
# Escreve no log central
with open(log_path, "a") as f:
f.write(json.dumps(log_entry) + "\n")
# O Gemini exige um JSON válido no stdout para hooks de AfterTool
print(json.dumps({}))
except Exception as e:
# Erros vão para o stderr para não quebrar o CLI
sys.stderr.write(f"Erro no Gemini Telemetry Hook: {str(e)}\n")
print(json.dumps({}))
if __name__ == "__main__":
log_telemetry()