Envie rastreamentos OpenTelemetry do seu aplicativo para a Veloz e veja a árvore de spans.
Métricas dizem que a latência subiu. Rastreamento diz onde. Cada requisição vira uma árvore de spans com a duração de cada etapa, atravessando os serviços do projeto.
Como funciona
Seu aplicativo envia os rastreamentos usando OpenTelemetry, o padrão aberto de instrumentação. A Veloz recebe, isola por organização e guarda por 7 dias.
Você não configura endereço nem credencial. Ao declarar traces no veloz.json, a Veloz injeta no serviço todas as variáveis que o OpenTelemetry precisa. As bibliotecas oficiais leem essas variáveis sozinhas.
A cada deploy de um serviço que declara traces, a Veloz define:
Variável
Valor
OTEL_EXPORTER_OTLP_ENDPOINT
Endereço de recebimento da sua organização
OTEL_EXPORTER_OTLP_HEADERS
Credencial de envio
OTEL_EXPORTER_OTLP_PROTOCOL
http/protobuf
OTEL_SERVICE_NAME
O nome do serviço no veloz.json
OTEL_RESOURCE_ATTRIBUTES
Projeto e serviço de origem
OTEL_TRACES_SAMPLER
parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG
O valor de sampleRate
Atenção: se você definir qualquer uma dessas variáveis, o seu valor prevalece e a Veloz não sobrescreve. Isso permite apontar o aplicativo para um coletor próprio, mas também é a causa mais comum de rastreamento que "para de funcionar" sem erro nenhum. Confira suas variáveis com veloz env list antes de investigar o código.
Protocolo
A Veloz recebe rastreamentos por OTLP sobre HTTP (http/protobuf). Não há endereço gRPC.
Isso importa porque vários SDKs do OpenTelemetry usam gRPC por padrão, e um exportador gRPC apontado para um endereço HTTP não gera erro visível: o aplicativo sobe normalmente, atende requisições e nenhum rastreamento chega.
O que fazer em cada linguagem:
Linguagem
Faça
Evite
Node/TS
@opentelemetry/exporter-trace-otlp-proto
@opentelemetry/exporter-trace-otlp-http (JSON)
Python
opentelemetry-instrument com as variáveis da Veloz
Definir OTEL_EXPORTER_OTLP_PROTOCOL como grpc
Go
otlptrace/otlptracehttp
otlptrace/otlptracegrpc
Java
O agente com as variáveis da Veloz
Definir OTEL_EXPORTER_OTLP_PROTOCOL como grpc
Se você não definir OTEL_EXPORTER_OTLP_PROTOCOL, a Veloz já define http/protobuf e as bibliotecas que respeitam essa variável fazem a coisa certa sozinhas. Em Go, a variável não decide nada: quem decide é o pacote de exportador que você importa.
Atenção, Node e TypeScript: aqui o pacote também decide o formato, e a variável não tem efeito. @opentelemetry/exporter-trace-otlp-http envia OTLP em JSON, que a Veloz recusa com 400 Bad Request. Use @opentelemetry/exporter-trace-otlp-proto, que envia protobuf. O nome do pacote engana: os dois falam HTTP, e o -proto é o que corresponde ao http/protobuf que a Veloz espera.
Essa falha é silenciosa por padrão. O aplicativo sobe, atende requisições e descarta os rastreamentos em segundo plano, porque o SDK do OpenTelemetry só registra o erro de exportação quando o diagnóstico está ligado. Para ver o erro:
import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ERROR);
Com o exportador errado, os logs do seu serviço passam a mostrar OTLPExporterError: Bad Request com código 400. Sem essa linha, não mostram nada.
// instrumentation.mjsimport { NodeSDK } from "@opentelemetry/sdk-node";import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";// O exportador lê OTEL_EXPORTER_OTLP_ENDPOINT e OTEL_EXPORTER_OTLP_HEADERS// do ambiente. A Veloz define essas variáveis automaticamente.const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter(), instrumentations: [getNodeAutoInstrumentations()],});sdk.start();
Importante: o arquivo de instrumentação precisa ser carregado antes do seu aplicativo. Use --import (Node 20.6 ou superior) ou --require em CommonJS. Um import "./instrumentation.js" no topo do seu index.js não funciona: nesse ponto as bibliotecas que precisam ser instrumentadas já foram carregadas.
As auto-instrumentações cobrem HTTP, Express, Fastify, pg, mysql2, ioredis e outras. Para marcar um trecho do seu próprio código:
pip install opentelemetry-distro opentelemetry-exporter-otlpopentelemetry-bootstrap -a install
# comando de start do serviçoopentelemetry-instrument python app.py
O opentelemetry-instrument lê OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS e OTEL_EXPORTER_OTLP_PROTOCOL do ambiente, definidas automaticamente pela Veloz. Ajuste o runtime.command do serviço no veloz.json para incluir o prefixo.
Go
go get go.opentelemetry.io/otel \ go.opentelemetry.io/otel/sdk \ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
package mainimport ( "context" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" sdktrace "go.opentelemetry.io/otel/sdk/trace")func iniciarRastreamento(ctx context.Context) (func(context.Context) error, error) { // Sem opções, o exportador lê OTEL_EXPORTER_OTLP_ENDPOINT e // OTEL_EXPORTER_OTLP_HEADERS do ambiente. exp, err := otlptracehttp.New(ctx) if err != nil { return nil, err } tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exp)) otel.SetTracerProvider(tp) return tp.Shutdown, nil}
Chame iniciarRastreamento no início do main e adie o desligamento, para que os spans em memória sejam enviados antes da saída. O trecho abaixo também usa o pacote log, então adicione "log" ao bloco de imports:
Atenção: em Go, o pacote importado decide o protocolo. otlptracegrpc não conecta ao endereço da Veloz e falha em silêncio, mesmo com OTEL_EXPORTER_OTLP_PROTOCOL definido como http/protobuf. Use sempre otlptracehttp.
Amostragem
sampleRate controla a fração de rastreamentos mantidos. A Veloz traduz esse valor para o amostrador parentbased_traceidratio, que decide uma vez por rastreamento: se um serviço decide manter, os serviços seguintes da mesma requisição mantêm também. A árvore nunca sai pela metade.
Atenção:sampleRate igual a 0 desliga o envio por completo. Nenhum rastreamento chega enquanto o valor for zero. Para desligar de propósito, prefira "enabled": false, que deixa a intenção explícita no arquivo.
Onde ver os dados
No painel, cada serviço web ou worker tem a aba Rastreamento, com a lista dos rastreamentos recentes, filtro de duração mínima e filtro de erros. Clicar em uma linha abre a árvore de spans, com o nome do serviço em cada linha, inclusive quando a requisição atravessa mais de um serviço do projeto.
Pelo terminal:
# Rastreamentos recentes, somente com erroveloz traces list --range 1h --errors# Árvore de spans de um rastreamentoveloz traces show 4bf92f3577b34da6# Estado do envioveloz obs status
Rastreamentos (última 1h, somente erros)
HORÁRIO OPERAÇÃO DURAÇÃO SPANS ID
14:02:11 POST /api/checkout 1.84s 23 4bf92f3577b34da6
13:58:40 POST /api/checkout 2.10s 21 a3ce929d0e0e4736
13:51:07 GET /api/orders/:id 940ms 11 9c2b7f14dd0a41e8
3 rastreamento(s) com erro de 41 no período.
Retenção
Rastreamentos ficam disponíveis por 7 dias. Os filtros de período no painel vão até 24 horas, porque uma busca de 7 dias é lenta e raramente útil. Para investigar algo além disso, prenda o identificador do rastreamento nos logs, que têm retenção maior.
Problemas comuns
Nada chega, o aplicativo sobe normalmente
Em ordem de frequência:
A instrumentação não é carregada antes do aplicativo. Em Node, use node --import ./instrumentation.mjs. Em Python, use opentelemetry-instrument como prefixo do comando de start.
Uma variável OTEL_* definida à mão. O seu valor prevalece sobre o da Veloz. Confira com veloz env list.
O serviço não recebeu tráfego. Sem requisição não há rastreamento.
veloz obs status identifica os casos 2 e 5 diretamente.
Os rastreamentos aparecem com o nome errado
OTEL_SERVICE_NAME é definido pela Veloz com o nome do serviço no veloz.json. Se você também define service.name no código, ao criar o recurso do SDK, o valor do código prevalece e o rastreamento aparece com outro nome. Remova a definição no código e deixe o SDK ler a variável.
Um serviço aparece na árvore e o outro não
A propagação de contexto entre serviços depende de o cliente HTTP também estar instrumentado. As auto-instrumentações cobrem os clientes mais comuns. Se você usa um cliente próprio, ele precisa propagar o cabeçalho traceparent.
O aplicativo perde os últimos spans ao reiniciar
Os spans ficam em um buffer antes do envio. Chame o desligamento do provedor ao receber o sinal de encerramento (tp.Shutdown em Go, sdk.shutdown() em Node) para esvaziar o buffer antes de sair.