Telemetria
O runner é instrumentado com OpenTelemetry, e a telemetria é no-op por default. Desenvolvimento, teste e CI não precisam de collector nenhum.
Ligar exige duas variáveis, não uma
Seção intitulada “Ligar exige duas variáveis, não uma”Esta é a pegadinha: a telemetria só fica ativa quando três condições valem ao mesmo tempo.
enabled = ROOTPILOT_OTEL_ENABLED && !OTEL_SDK_DISABLED && exporter !== 'none'E o exporter é none por default. Ou seja:
# NÃO basta: o exporter continua `none` e nada é emitidoROOTPILOT_OTEL_ENABLED=true
# funcionaROOTPILOT_OTEL_ENABLED=trueROOTPILOT_OTEL_EXPORTER=otlpOTEL_EXPORTER_OTLP_ENDPOINT=https://otel.example.comVariáveis
Seção intitulada “Variáveis”| Variável | Default | Notas |
|---|---|---|
ROOTPILOT_OTEL_ENABLED |
false |
Master switch. Aceita true ou 1. |
ROOTPILOT_OTEL_EXPORTER |
none |
otlp, console ou none. Precisa sair de none. |
OTEL_SDK_DISABLED |
false |
O kill-switch padrão do OTel. true vence o master switch. |
OTEL_EXPORTER_OTLP_ENDPOINT |
nenhum | URL do collector. |
OTEL_EXPORTER_OTLP_HEADERS |
nenhum | Headers, para collector autenticado. |
OTEL_EXPORTER_OTLP_PROTOCOL |
http/protobuf |
|
OTEL_SERVICE_NAME |
rootpilot-runner |
Cravado na imagem do runner. |
OTEL_SERVICE_VERSION |
versão da imagem | Preenchido no build a partir da tag da release. Ausente ⇒ o atributo service.version simplesmente não é emitido. |
RUNNER_INSTANCE_ID |
UUID por boot | Vira service.instance.id — é o que separa um runner do outro quando a frota exporta pro mesmo collector. |
OTEL_TRACES_SAMPLER_ARG |
1 |
Razão de amostragem, entre 0 e 1. |
O console é útil para verificar rapidamente se a instrumentação está viva sem subir collector.
Como a inicialização acontece
Seção intitulada “Como a inicialização acontece”Por preload, e não por chamada em código:
node --import @rootpilotsh/otel/register apps/runner/dist/index.jsO preload roda antes de o grafo de módulos da aplicação carregar, o que é o que permite à
auto-instrumentação do OTel aplicar patch em http e ws primeiro. O CMD da imagem do runner já é
exatamente esse comando, então você não precisa fazer nada além de setar as variáveis.
É também por isso que nome e versão do serviço vêm do ambiente: nesse ponto não existe build que os inline nem chamador em código para passá-los.
O que é instrumentado
Seção intitulada “O que é instrumentado”| Span | Quando |
|---|---|
rootpilot.handle_invoke_batch |
Recebimento de um lote de invocações, sob o contexto remoto extraído do metadata da mensagem. |
rootpilot.run_op |
Cada operação, com atributos de MCP e da operação. |
rootpilot.tunnel.session |
Uma sessão de túnel inteira — da tentativa de conexão até o fechamento, com o resultado do handshake e por que a sessão terminou. |
O último responde a pergunta que o lado do servidor não consegue: o túnel enxerga que a conexão
caiu, e o motivo mora no runner. Cada sessão fecha com um rootpilot.tunnel.end_reason de vocabulário
fechado — e o ponto dele é separar o que nós provocamos do que sofremos:
end_reason |
Significa |
|---|---|
cert_rotated |
A renovação de cert trocou a identidade e forçou reconexão. Esperado, e frequente: a renovação roda a 2/3 da vida de um cert de ≤1h. |
runner_stopping |
Drain (SIGTERM, spot reclaim). Esperado. |
server_closed |
O control-plane fechou limpo (código 1000/1001). |
network_error |
Queda sem frame de close (1006 e afins). |
heartbeat_timeout |
Dois intervalos de heartbeat sem resposta — o runner derrubou e reconectou. |
hello_rejected / version_incompatible |
O handshake foi recusado. |
Sem essa separação, as reconexões por rotação de cert — dezenas por dia, por desenho — entram na mesma conta do churn que se quer investigar, e o número nunca fecha.
Métricas irmãs: rootpilot.tunnel.session.duration e rootpilot.tunnel.reconnects.total, ambas
rotuladas pelo mesmo end_reason.
O contexto remoto é o detalhe que importa: ele faz o trace ser distribuído, unindo o span do control-plane ao do runner num único trace. Também há métricas por tenant.
Correlação com os logs
Seção intitulada “Correlação com os logs”O logger correlaciona trace_id e span_id nas linhas de log. Com a telemetria ligada, dá para sair
de um span no seu backend direto para as linhas de log daquela operação.
Flush no encerramento
Seção intitulada “Flush no encerramento”A telemetria é drenada no SIGTERM, depois do drain do runner e independentemente de ele ter dado
certo — um drain que falha não apaga a telemetria que explicaria a falha.
Ainda assim, o flush final não é a única linha de defesa: as métricas são exportadas a cada 15s, e não no default de 60s do SDK. A diferença importa para gado de vida curta — um runner que morre antes de completar um ciclo de export dependeria inteiramente do flush.
Observabilidade da frota, sem OTel
Seção intitulada “Observabilidade da frota, sem OTel”Mesmo com a telemetria desligada, o runner reporta estado ao control-plane pelo handshake:
accounts[]: o contexto de nuvem derivado no boot.ConnectorStatus{served, health}: o que cada connector está servindo e como passou no self-check de credencial.ConnectorStatus.ops: quais operações aquele connector serve.
O último existe porque uma lista plana de operações servidas não diz de quem elas são, e sem o dono, o control-plane não consegue descontar as operações de um connector com credencial quebrada.