Endpoints de health
| Serviço | Endpoint | Retorna |
|---|---|---|
| app | GET /api/health | {"status":"ok","timestamp":"..."} |
| realtime | GET /health na porta 3002 | {"status":"ok","timestamp":"...","connections":0} |
/api/health é apenas um sinal de liveness. Ele retorna 200 enquanto o processo estiver servindo HTTP — não verifica o banco de dados, o Redis nem o object storage. Uma resposta saudável não significa que o app consegue atender tráfego com sucesso, então não trate isso como verificação de dependências. Verifique as dependências com o teste de fumaça.
Probes do Kubernetes
O chart traz probes ajustados para o cold start do Next.js. Padrões do app:
| Probe | Caminho | Orçamento |
|---|---|---|
startupProbe | / | 60 × 5s = 5 minutos para ficar pronto |
livenessProbe | / | 6 × 30s = 180s de falha antes de reiniciar |
readinessProbe | / | 3 × 10s = ~30s para receber tráfego |
O realtime usa /health na porta 3002, com um orçamento de inicialização de 150 segundos.
O orçamento generoso de inicialização importa: um cold start do Next.js com um bundle grande pode levar minutos, e um liveness probe mais apertado reinicia o pod no meio do boot, em loop. Se você customizar os probes, mantenha o orçamento de inicialização bem acima do cold start que você observa.
app:
startupProbe:
httpGet:
path: /
port: 3000
periodSeconds: 5
failureThreshold: 60Logs
Os dois serviços registram JSON estruturado no stdout. Colete com o que você já usa — Fluent Bit, Vector, Datadog Agent, Loki.
Em builds de produção o logger usa ERROR por padrão, e o chart do Helm não define LOG_LEVEL para o app nem para o realtime. Até você aumentar o nível, só aparecem erros nos logs — e é por isso que um deployment aparentemente saudável pode parecer não registrar nada. Defina LOG_LEVEL: "info" ao colocar um deployment em operação ou ao investigar um problema.
kubectl logs -n studio -l app.kubernetes.io/component=app --tail=200 -f
kubectl logs -n studio -l app.kubernetes.io/component=realtime --tail=200 -f
kubectl logs -n studio deploy/studio-app -c migrations --tail=100docker compose -f docker-compose.prod.yml logs -f studioToda requisição de API carrega um request ID que aparece em todas as linhas de log daquela requisição — o jeito mais rápido de reconstruir uma chamada que falhou.
Os logs de execução de workflow são uma superfície separada, de produto, armazenada no banco de dados e visível em Logs no app. Eles não são a mesma coisa que os logs de contêiner: use os logs de contêiner para problemas de infraestrutura e Logs para o comportamento dos workflows.
Ocultar dados pessoais nos logs
Ative o serviço de PII e a ocultação nos logs se as execuções puderem conter dados sensíveis:
pii:
enabled: true
app:
env:
PII_REDACTION: "true"
INTERNAL_API_BASE_URL: "http://studio-app.studio.svc.cluster.local:3000"Veja Segurança para o requisito de INTERNAL_API_BASE_URL — sem um valor acessível dentro do cluster, o caminho falha de forma fechada.
Telemetria anônima
O Studio envia telemetria de uso anônima por padrão. Traces de OpenTelemetry são exportados para https://telemetry.seeyu.ai/v1/traces, a menos que você desative isso. Deployments com auto-hospedagem que têm política de egresso devem decidir sobre isso explicitamente.
O que é coletado, conforme apps/web/telemetry.config.ts: estatísticas de uso de recursos, taxas de erro, métricas de desempenho (amostradas em 10%) e traces de operações de IA/LLM. O que não é coletado: informações pessoais, conteúdo ou saídas de workflow, chaves de API ou tokens, e endereços IP ou geolocalização.
Três formas de mudar isso:
# Disable entirely
NEXT_TELEMETRY_DISABLED=1
# Or redirect to your own OTLP collector instead of Studio's
TELEMETRY_ENDPOINT=http://otel-collector.observability.svc.cluster.local:4318/v1/tracesCada pessoa também pode desativar individualmente em Settings → Privacy → Allow anonymous telemetry.
Tracing
O Studio emite traces de OpenTelemetry. O chart também pode fazer o deploy de um collector para você:
telemetry:
enabled: trueOu aponte o app para um collector que você já mantém:
| Variável | Finalidade |
|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | Endpoint do collector (OTLP) |
OTEL_EXPORTER_OTLP_HEADERS | Headers de autenticação, key=value separados por vírgula |
OTEL_TRACES_SAMPLER_ARG | Taxa de amostragem |
OTEL_DEPLOYMENT_ENVIRONMENT | Rótulo de ambiente nos spans emitidos |
TELEMETRY_SAMPLING_RATIO | Taxa de amostragem no nível da aplicação |
TELEMETRY_ENDPOINT | Endpoint de telemetria customizado |
Especificamente para o Grafana Cloud:
| Variável | Finalidade |
|---|---|
GRAFANA_OTLP_ENDPOINT | Endpoint OTLP do Grafana |
GRAFANA_OTLP_HEADERS | Por exemplo Authorization=Basic <base64(instanceId:token)> |
GRAFANA_DEPLOYMENT_ENVIRONMENT | Rótulo do tier de deployment |
Se você ativar a exportação para o Jaeger no chart, aponte telemetry.jaeger.endpoint para a porta OTLP gRPC (4317) do Jaeger — o collector exporta via OTLP.
Métricas
As imagens padrão do app e do realtime não expõem um endpoint /metrics. A opção monitoring.serviceMonitor do chart existe para builds que expõem — ativá-la com as imagens de fábrica gera um ServiceMonitor que não coleta nada.
Até que um endpoint de métricas da aplicação exista, construa os alertas a partir dos sinais que já existem:
- Estado do Kubernetes — reinícios de pod,
CrashLoopBackOff, OOMKills, contagem de réplicas versus desejada, uso de PVC (kube-state-metrics). - Ingress/load balancer — taxa de requisições, taxa de 5xx, latência p99, contagem de conexões websocket.
- PostgreSQL — número de conexões versus
max_connections, atraso de replicação, uso de disco, queries de longa duração. - Redis — uso de memória, evictions, clientes conectados.
- CronJobs — última conclusão bem-sucedida de cada job.
O que monitorar com alertas
| Alerta | Por que importa |
|---|---|
| Loop de reinício do pod do app / OOMKilled | Memória é o recurso limitante; OOMKills significam que execuções estão morrendo no meio |
| Um CronJob não teve sucesso em cerca de 3× o próprio intervalo de agendamento | Workflows agendados e triggers de polling estão mortos silenciosamente. Defina o limite por job — os jobs de cada minuto justificam ~15 minutos; os de hora em hora, duas vezes ao dia e diários precisam de janelas proporcionalmente maiores |
| Taxa de 5xx no ingress acima da linha de base | Impacto amplo nas pessoas |
Conexões do Postgres acima de 80% de max_connections | A próxima réplica ou pico de tráfego vai começar a falhar |
| Disco do Postgres acima de 80% | Os embeddings da base de conhecimento crescem de forma constante |
| Redis inacessível | A colaboração ao vivo e as atualizações de status param, sem erros no app |
| Certificado expirando em 14 dias | Especialmente com certificados gerenciados manualmente |
| Taxa de 4xx/5xx no object storage | Uploads quebrados normalmente aparecem aqui primeiro |
O alerta de CronJob é o que mais falta e o de que mais se precisa. Falhas de jobs em background não produzem erro visível — os agendamentos simplesmente param de disparar. Alerte quando kube_cronjob_status_last_successful_time estiver atrasado, com um limite por job derivado do agendamento daquele job.
Diagnóstico rápido
# Overall state
kubectl get pods,cronjobs -n studio
# Why is a pod unhealthy
kubectl describe pod -n studio <pod>
# Did migrations succeed
kubectl logs -n studio deploy/studio-app -c migrations --tail=100
# Are background jobs running
kubectl get jobs -n studio --sort-by=.metadata.creationTimestamp | tail
# Resource pressure
kubectl top pods -n studio