As métricas dizem-te que algo está lento. Os logs dizem-te que ocorreu um erro. Nenhum deles te diz porquê um pedido que passou por seis serviços demorou três segundos quando deveria ter demorado duzentos milissegundos. É esse o problema que o tracing distribuído resolve, e o Tempo é como o adicionei ao stack.
Resisti ao tracing durante algum tempo. Parecia o tipo de coisa que grandes empresas com dezenas de microsserviços precisavam, não algo relevante para os sistemas que eu estava a construir. Estava errado. Mesmo com três ou quatro serviços a comunicar entre si, o tracing responde a perguntas que métricas e logs simplesmente não conseguem.
O Que o Tracing Realmente Mostra
Cada pedido que entra no teu sistema recebe um trace ID. À medida que esse pedido flui pelos teus serviços, cada operação — uma query à base de dados, uma chamada HTTP a outro serviço, uma consulta à cache — torna-se um span dentro desse trace. No final, tens uma imagem completa de tudo o que aconteceu, por ordem, com os tempos de cada passo.
A primeira vez que olhei para um trace real de um pedido lento, encontrei o problema em cerca de trinta segundos. Uma query à base de dados que deveria ter usado um índice estava a fazer um full table scan. Apareceu como um span a demorar 1,8 segundos num pedido que era de outra forma normal. Sem tracing estaria a adicionar log statements e a fazer redeploy, na esperança de o apanhar de novo.
O Lugar do Tempo no Stack
O Tempo é o backend de tracing distribuído da Grafana. Como o Loki com os logs, foi desenhado para ser barato de correr e simples de operar. Armazena traces em object storage — S3, Azure Blob, ou disco local — e indexa apenas o trace ID e um conjunto reduzido de campos de metadados. Não pagas por indexing completo de cada atributo de span.
Aceita traces nos formatos standard: OpenTelemetry, Jaeger, Zipkin. Escolhes o que a tua aplicação emite e o Tempo recebe. Sem protocolo proprietário.
E como o Prometheus e o Loki antes dele, liga-se diretamente ao Grafana. Uma vez configurado, podes saltar de um pico na taxa de erros diretamente para os traces daquela janela temporal. A partir daí fazes drill-down em spans individuais. Toda a investigação acontece num só lugar.
Configuração em .NET
O OpenTelemetry é o standard e o .NET tem suporte de primeira classe. Adiciona os packages:
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocolDepois configura no startup:
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.SetResourceBuilder(ResourceBuilder.CreateDefault()
.AddService("my-api"))
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter(opts => {
opts.Endpoint = new Uri("http://tempo:4317");
}));É tudo para o básico. A instrumentação do ASP.NET Core cria automaticamente spans para cada pedido recebido. A instrumentação do HTTP client cria spans filhos para cada chamada de saída. Tens o fluxo completo do pedido sem escrever um único span manual.
Para chamadas à base de dados, adiciona o package de instrumentação relevante para o teu ORM. O Entity Framework Core tem um. O SqlClient tem um. Encaixam na mesma configuração.
Ler Traces no Grafana
Adiciona o Tempo como fonte de dados no Grafana, aponta-o para a tua instância Tempo, e podes começar a pesquisar traces imediatamente. Filtra por nome de serviço, duração, estado, ou qualquer atributo que a tua aplicação adicione aos spans.
A vista em cascata é onde o tracing se torna visivelmente útil. Vês cada span disposto numa linha temporal, indentado pela relação pai-filho. Uma query lenta à base de dados aparece visualmente dentro do handler que a desencadeou. Uma retry a um serviço externo aparece como dois spans consecutivos com um intervalo entre eles. A forma do trace conta-te a história do pedido.
O Grafana também liga o Tempo ao Loki automaticamente se configurares ambos. A partir de um span, podes saltar diretamente para os logs emitidos durante a janela temporal desse span, para aquele serviço, sem qualquer filtragem manual. Correlação que antes exigia copiar e colar timestamps entre três separadores é agora um clique.
O Que Não Esperava
Esperava que o tracing fosse útil para depurar pedidos lentos. É. Mas o que me surpreendeu foi a utilidade para compreender o comportamento normal do sistema.
Olhar para traces saudáveis — pedidos que completaram rapidamente e sem erros — ensinou-me coisas sobre o código que eu não sabia. Queries à base de dados que pensava serem simples envolviam afinal múltiplas rondas. Uma cache que assumia estar a ser atingida estava na realidade a falhar no primeiro pedido de cada sessão. Aprendes o que o teu sistema realmente faz, não o que pensas que faz.
Esse tipo de compreensão é difícil de obter só com métricas e logs. As métricas agregam tudo. Os logs capturam eventos específicos. Os traces mostram-te a estrutura da execução. Os três juntos dão-te uma imagem completa.
Completar o Stack
Com Prometheus, Loki, Tempo e Grafana, o stack de observabilidade está completo. Métricas para o quê, logs para o quê exatamente, traces para o porquê e onde. Tudo open source, tudo vendor-neutral, tudo ligado no Grafana.
Configurei isto de forma incremental ao longo de vários meses, começando com o Prometheus, depois adicionando o Loki, depois o Tempo. Cada um adicionou uma camada de compreensão que os anteriores não conseguiam fornecer. Nenhum deles exigiu investimento significativo em infraestrutura ou lock-in no modelo de faturação de um fornecedor de cloud.
Se já chegaste até aqui com o Prometheus e o Loki, o Tempo é um último passo natural e que vale a pena.



