O Grafana Tempo no Node.js é uma opção para armazenar e consultar traces distribuídos produzidos por OpenTelemetry. A aplicação instrumentada envia spans por OTLP para um Collector ou Grafana Alloy, que encaminha os dados ao Tempo. No Grafana, a equipe pesquisa traces, analisa o caminho crítico e relaciona erros com logs e métricas.
Tempo foi projetado para alto volume e armazenamento econômico em object storage. Em vez de indexar todos os atributos de forma tradicional, ele combina metadados, blocos compactados e TraceQL para localizar operações. Essa arquitetura reduz custo, mas exige planejamento de retenção, cardinalidade, sampling e capacidade de consulta.
Neste guia, você aprenderá a executar Tempo, enviar traces de uma aplicação Node.js, configurar OpenTelemetry Collector, usar TraceQL, criar métricas a partir de spans e correlacionar Tempo com Loki e Prometheus.
O que é Grafana Tempo?
A documentação oficial do Grafana Tempo descreve o projeto como um backend open source de distributed tracing, integrado ao Grafana, Loki, Prometheus, Mimir e OpenTelemetry. Ele aceita protocolos como OTLP, Jaeger e Zipkin.
Os principais objetivos são:
- armazenar traces em grande escala;
- usar object storage;
- consultar com TraceQL;
- gerar métricas a partir de spans;
- correlacionar traces, logs e métricas;
- reduzir custo operacional.
Arquitetura básica
Um fluxo comum:
- a aplicação Node.js cria spans;
- o SDK exporta OTLP;
- OpenTelemetry Collector recebe e aplica batch;
- o Collector envia ao distributor do Tempo;
- Tempo grava blocos em object storage;
- Grafana consulta por TraceQL ou trace ID.
Consulte OpenTelemetry no Node.js para instrumentação.
Executando localmente
Para desenvolvimento, use Docker Compose com Tempo e Grafana. Uma configuração simplificada:
services:
tempo:
image: grafana/tempo:latest
command: ["-config.file=/etc/tempo.yaml"]
volumes:
- ./tempo.yaml:/etc/tempo.yaml
ports:
- "3200:3200"
- "4317:4317"
- "4318:4318"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"Em produção, fixe versões específicas e use armazenamento persistente.
Configuração monolítica mínima
server:
http_listen_port: 3200
stream_over_http_enabled: true
storage:
trace:
backend: local
local:
path: /var/tempo/blocks
wal:
path: /var/tempo/wal
distributor:
receivers:
otlp:
protocols:
grpc:
http:O backend local serve apenas para desenvolvimento. Produção deve usar S3, Google Cloud Storage, Azure Blob ou outro backend suportado.
Instrumentando Node.js
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: 'http://otel-collector:4318/v1/traces'
}),
instrumentations: [getNodeAutoInstrumentations()]
});
sdk.start();Inicialize o SDK antes de carregar frameworks e clientes de banco.
Enviando pelo Collector
receivers:
otlp:
protocols:
grpc:
http:
processors:
memory_limiter:
check_interval: 1s
limit_mib: 256
batch:
timeout: 5s
exporters:
otlp/tempo:
endpoint: tempo:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/tempo]O artigo OpenTelemetry Collector no Node.js detalha filas, retry e sampling.
Resource attributes
OTEL_SERVICE_NAME=orders-api
OTEL_RESOURCE_ATTRIBUTES=service.version=2.3.0,deployment.environment.name=productionUse atributos estáveis. Evite request ID, user ID ou valores únicos como resource attributes.
Spans manuais
import { trace, SpanStatusCode } from '@opentelemetry/api';
const tracer = trace.getTracer('orders-domain');
export async function createOrder(input) {
return tracer.startActiveSpan('order.create', async span => {
try {
span.setAttribute('order.items_count', input.items.length);
const order = await service.execute(input);
return order;
} catch (error) {
span.recordException(error);
span.setStatus({ code: SpanStatusCode.ERROR });
throw error;
} finally {
span.end();
}
});
}Nomeie operações de forma estável. Não coloque IDs no nome do span.
Configurando data source no Grafana
No Grafana, adicione uma fonte Tempo apontando para:
http://tempo:3200Ative links para Loki e Prometheus quando os sistemas compartilham trace IDs e exemplars.
Pesquisa por trace ID
Quando um log contém trace ID, copie o valor e abra o trace diretamente. Essa é a forma mais precisa de consulta e exige pouco trabalho de busca.
TraceQL
TraceQL permite selecionar traces por atributos e estrutura:
{ resource.service.name = "orders-api" }Erros:
{ status = error }Latência:
{ duration > 1s }Combinando:
{ resource.service.name = "orders-api" && duration > 500ms }Atributos dedicados
Tempo pode usar dedicated attribute columns para acelerar consultas frequentes. Escolha atributos de baixa ou média cardinalidade:
- service name;
- deployment environment;
- HTTP route;
- status code;
- operation type.
Não transforme IDs únicos em colunas sem avaliar custo.
TraceQL metrics
TraceQL metrics gera séries a partir de traces, como contagem, taxa de erros e latência. Isso ajuda a explorar comportamento sem instrumentar toda métrica manualmente.
Para SLOs críticos, métricas explícitas continuam importantes. Consulte Métricas Prometheus no Node.js.
Metrics generator
O metrics-generator pode produzir:
- span metrics;
- service graphs;
- métricas customizadas;
- exemplars.
Controle cardinalidade. Atributos como URL completa ou user ID criam séries demais.
Service graphs
Service graphs mostram relações entre serviços, taxa, erros e duração. Eles dependem de propagação correta do contexto e nomes de serviço consistentes.
Exemplars
Prometheus exemplars conectam um ponto de métrica a um trace ID. No Grafana, um pico de latência pode abrir um trace representativo.
Correlação com Loki
Inclua trace ID nos logs:
const context = trace.getActiveSpan()?.spanContext();
logger.info({
traceId: context?.traceId,
spanId: context?.spanId
}, 'Pedido processado');Veja Grafana Loki no Node.js.
Sampling
Armazenar todos os traces pode ser caro. Estratégias:
- probabilístico no SDK;
- parent-based;
- tail sampling no Collector;
- manter erros;
- manter traces lentos;
- amostra base do tráfego normal.
Sampling agressivo pode esconder problemas raros. Meça volume e custo antes de reduzir.
Object storage
Produção deve usar armazenamento durável. Configure:
- bucket separado;
- criptografia;
- lifecycle;
- credenciais mínimas;
- replicação quando necessário;
- alertas de capacidade.
Retenção
Defina retenção conforme investigação e custo. Traces detalhados podem ter prazo menor que métricas agregadas. Não mantenha indefinidamente dados sensíveis.
Modos de deployment
Tempo oferece modos monolítico e distribuído. Monolítico é simples para desenvolvimento e pequena escala. Distribuído separa distributor, ingester, querier, compactor e outros componentes para escalar independentemente.
Alta disponibilidade
Em produção:
- múltiplas réplicas;
- object storage durável;
- replicação adequada;
- zone awareness quando suportado;
- limits por tenant;
- monitoramento interno;
- rollback de versões.
Multi-tenancy
Tempo suporta tenant IDs em arquiteturas apropriadas. O proxy de autenticação deve validar o tenant e impedir que clientes escolham livremente outra organização.
Segurança
- Use TLS em OTLP.
- Não exponha ingestão publicamente.
- Autentique collectors.
- Restrinja Grafana.
- Remova dados pessoais.
- Proteja object storage.
- Audite consultas.
Monitorando Tempo
Acompanhe:
- spans recebidos;
- spans recusados;
- latência de ingestão;
- erros de object storage;
- compactação;
- fila de consultas;
- cache hit;
- memória e CPU;
- tempo de busca.
Cardinalidade
Mesmo que Tempo seja econômico, atributos excessivos aumentam tamanho e custo de busca. Evite:
- payload completo;
- headers sensíveis;
- SQL com valores;
- URLs com IDs;
- mensagens de erro como dimensão;
- objetos serializados.
Teste de carga
Gere tráfego representativo e observe se o Collector ou Tempo descarta spans. Use Autocannon no Node.js ou k6 e compare volume, latência e custo.
Erros comuns
- Enviar direto sem Collector: retry fica na aplicação.
- Sem sampling: custo cresce rapidamente.
- Sampling excessivo: incidentes desaparecem.
- Trace ID fora dos logs: correlação fica manual.
- Atributos com PII: dados sensíveis são armazenados.
- Backend local em produção: traces são perdidos.
- Sem métricas internas: ingestão falha silenciosamente.
- Nomes de serviço inconsistentes: service graph fragmenta.
Conclusão
O Grafana Tempo no Node.js fornece armazenamento escalável para traces OpenTelemetry e integra consultas, service graphs, métricas e correlação com logs.
Envie OTLP por Collector, controle sampling, proteja atributos e use object storage durável. Com TraceQL e links entre Loki, Prometheus e Tempo, a equipe consegue partir de um alerta e chegar ao caminho exato da requisição que apresentou falha.




