Fazer deploy ↗
docs

Rastreamento

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.

{
  "services": {
    "apps/api": {
      "id": "svc_api456",
      "name": "loja-api",
      "type": "web",
      "root": "apps/api",
      "runtime": {
        "command": "node dist/index.js",
        "port": 3000
      },
      "observability": {
        "traces": { "sampleRate": 1 }
      }
    }
  }
}
Campo Padrão Descrição
enabled true false desliga o envio sem apagar a configuração
sampleRate 1 Fração de rastreamentos mantidos, de 0 a 1

Para ligar com os padrões, "traces": {} já basta.

Variáveis definidas automaticamente

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.

Node e TypeScript

npm install @opentelemetry/api \
  @opentelemetry/sdk-node \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-proto
// instrumentation.mjs
import { 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();
{
  "scripts": {
    "start": "node --import ./instrumentation.mjs dist/index.js"
  }
}

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:

import { trace } from "@opentelemetry/api";
 
const tracer = trace.getTracer("loja-api");
 
await tracer.startActiveSpan("calcularFrete", async (span) => {
  try {
    return await calcularFrete(pedido);
  } finally {
    span.end();
  }
});

Python

pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
# comando de start do serviço
opentelemetry-instrument python app.py

O opentelemetry-instrumentOTEL_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 main
 
import (
	"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:

func main() {
	ctx := context.Background()
 
	shutdown, err := iniciarRastreamento(ctx)
	if err != nil {
		log.Fatal(err)
	}
	defer shutdown(ctx)
 
	// ...
}

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.

{
  "observability": {
    "traces": { "sampleRate": 0.25 }
  }
}

Como escolher:

Volume do serviço Valor sugerido
Baixo, ambiente de teste 1
Moderado 0.25
Alto, milhares de req/min 0.05

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 erro
veloz traces list --range 1h --errors
 
# Árvore de spans de um rastreamento
veloz traces show 4bf92f3577b34da6
 
# Estado do envio
veloz 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:

  1. 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.
  2. sampleRate igual a 0. Nada é enviado com zero.
  3. Exportador gRPC. Veja Protocolo.
  4. Uma variável OTEL_* definida à mão. O seu valor prevalece sobre o da Veloz. Confira com veloz env list.
  5. 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.

Próximos passos