feat(monitorizacao): scripts de saúde e telemetria do Gemini CLI
This commit is contained in:
@@ -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"
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
Reference in New Issue
Block a user