Observabilidade compartilhada: uma stack Grafana para todos os meus projetos
Centralizar a observabilidade de múltiplas aplicações .NET em uma stack externa com Grafana, Tempo, Prometheus e Loki reduz consumo de RAM, simplifica monitoramento e traz insights profundos via tracing distribuído.

Em projetos onde várias aplicações .NET são independentes, uma prática comum, mas problemática, que vejo é cada uma subir sua própria instância de ferramentas de observabilidade, como Seq ou Grafana, consumindo RAM substancial em VPSs muitas vezes limitadas, sem uso efetivo dessas ferramentas. Eu chamo esse comportamento de "isolamento custoso": cada time tem seu Grafana individual, que acaba subutilizado e gerando custos que se somam.
Aqui surge a bifurcação: manter várias pilhas separadas de observabilidade, ou inverter a lógica e centralizar tudo numa stack externa compartilhada. A escolha deve considerar critérios claros:
- Consumo de recursos e custos operacionais – Quanto RAM/CPU a solução demanda? Quanto impacta a infraestrutura?
- Escalabilidade e manutenção – Como facilitar upgrades, backups e monitoramento geral?
- Unificação dos dados para correlação – É possível correlacionar traces, logs e métricas entre projetos?
- Complexidade na aplicação – A aplicação precisa hospedar algo além do seu domínio ou apenas emitir dados?
- Agilidade para diagnósticos – A centralização agiliza a descoberta de problemas complexos?
Vamos destrinchar as duas opções.
Pilha individual por projeto: o que ela cobra e entrega
Cada projeto sobe seu Grafana, seu Seq (ou equivalente), possivelmente um Prometheus local e algum coletor para logs. Isso entrega um ambiente isolado onde a equipe tem controle completo. Contudo, isso cobra:
- Consumo elevado de RAM e CPU: Cada instância exige sua fatia de memória, multiplicando o consumo. VPSs comuns sofrem para manter tudo estável.
- Manutenção multiplicada: Atualizar, configurar e monitorar várias pilhas demanda horas e foco repetido, que poderia ser melhor investido no negócio.
- Dados isolados: Dificulta encontrar causas em sistemas distribuídos ou correlacionar eventos entre microserviços.
- Sobrecarga na configuração da aplicação: A aplicação precisa se preocupar em configurar múltiplos endpoints, agentes e roteamento para sua própria pilha.
No .NET, muitas vezes acaba assim: a aplicação emite logs estruturados no Seq via Serilog, ou expõe métricas para um Prometheus local, tudo bem redundante. Isso cria "ilhas" de observabilidade, de difícil cruzamento.
Stack Grafana compartilhada externa: o que ela cobra e entrega
Aqui a aplicação adere ao modelo “emit-only”: ela só gera dados e envia para a stack externa, que é única para todos os projetos. Isso muda tudo:
- Consumo otimizado de recursos: A stack é única, gerida em um único cluster ou VPS com Docker numa rede externa compartilhada, aproveitando melhor RAM e CPU.
- Manutenção centralizada: Atualizações são feitas em um só lugar, simplificando operações e garantindo uniformidade.
- Correlação avançada: Traces, logs e métricas centralizados possibilitam diagnósticos de ponta a ponta.
- Simplicidade na aplicação: A aplicação só configura emissões via OpenTelemetry (OTLP para traces), endpoint /metrics (Prometheus) e logs no stdout para coleta via Loki.
- Escalabilidade: Adicionar projetos é questão de configurar emissão dos dados, sem subir novas instâncias de stack.
A parte que cobra é a configuração inicial da stack de observabilidade com containers, rede Docker externa e a padronização dos emissores na aplicação.
Configuração mínima .NET para emissão
Para emitir traces e logs que alimentam esta stack, a aplicação pode usar OpenTelemetry e Serilog assim:
// Startup.cs ou Program.cs (ASP.NET Core 6+)
builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder =>
{
tracerProviderBuilder
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("http://tempo:4317");
});
});
builder.Services.AddOpenTelemetryMetrics(metricsBuilder =>
{
metricsBuilder
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddPrometheusExporter();
});
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console(new Serilog.Formatting.Compact.RenderedCompactJsonFormatter()) // saída estruturada para Loki
.CreateLogger();
builder.Host.UseSerilog();
Com essas configurações, a aplicação:
- Emite traces via OTLP para o Tempo (collector).
- Expõe métricas para serem recolhidas via scrape pelo Prometheus.
- Imprime logs estruturados JSON no stdout, que serão coletados pelo Loki.
Rede Docker externa e stack compartilhada
Use uma rede Docker externa para isolar a comunicação da observability stack:
docker network create observability_network
Em cada docker-compose dos serviços da stack (Tempo, Prometheus, Loki, Grafana), conecte-os a essa rede:
networks:
observability_network:
external: true
services:
grafana:
image: grafana/grafana
networks:
- observability_network
tempo:
image: grafana/tempo
networks:
- observability_network
prometheus:
image: prom/prometheus
networks:
- observability_network
loki:
image: grafana/loki
networks:
- observability_network
Assim, o endereço tempo:4317 ou loki:3100 é resolvível dentro da rede para as aplicações enviarem dados. Isso promove isolamento e segurança da stack sem expor portas no host.
Exemplo de estrutura de request logging pesquisável no Loki
Para aproveitar o Loki, o log precisa ser estruturado e enxergável por campos. Com Serilog e o formatter acima, a linha de log JSON fica assim:
{
"Timestamp": "2024-06-21T14:32:45.123Z",
"Level": "Information",
"MessageTemplate": "Request completed in {ElapsedMilliseconds}ms",
"ElapsedMilliseconds": 238,
"RequestPath": "/api/orders",
"UserId": "42",
"StatusCode": 200
}
Essa estrutura facilita buscas por campos (ex: filtrar por statusCode ou UserId) no Grafana Loki, expondo a consulta a log detalhada e precisa.
Antes e depois: vantagens no custo e no diagnóstico
Antes: múltiplas pilhas, cada uma consumindo entre centenas de MB a GB de RAM; pequenos VPSs saturados; pouca integração entre logs, métricas e traces; investigação limitada a logs pontuais.
Depois: stack única consumindo substancialmente menos RAM no total; facilidade em escalar ou migrar a observability stack; diagnósticos via tracing distribuído, revelando latências causadas por chamadas HTTP internas ou falhas em cadeia que antes passavam despercebidas.
Quando usar cada uma
- Múltiplas pilhas isoladas podem ser úteis quando projetos são completamente independentes e times querem autonomia máxima, mas paga-se com altos custos e empobrecimento do monitoramento.
- A stack compartilhada é ideal para ecossistemas crescentes, projetos relacionados ou microserviços que se beneficiam da visão unificada.
Se seu ambiente for de equipes completamente desconectadas e com requisitos rígidos de isolamento absoluto, aí sim a pilha individual será o caminho, mesmo com o custo operacional. Para quase tudo que envolve integração, centralizar a observabilidade traz ganhos significativos.
Fontes oficiais para aprofundamento:
- OpenTelemetry .NET: https://learn.microsoft.com/pt-br/dotnet/core/diagnostics/observability-with-otel
- Prometheus exposer no .NET: https://github.com/prometheus-net/prometheus-net
- Loki + Serilog: https://github.com/serilog-contrib/serilog-sinks-grafana-loki
- Grafana com Docker: https://grafana.com/docs/grafana/latest/installation/docker/
Adotar uma pilha Grafana compartilhada externa para as aplicações .NET é, na minha experiência, o caminho para uma observabilidade eficiente, econômica e rica em insights. A inversão do modelo para “aplicação só emite” simplifica o desenvolvimento e libera time para focar no que realmente importa: entregar valor com software confiável.