Observabilidade .NET Grafana OpenTelemetry observabilidade Docker Serilog tracing distribuído logs estruturados

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.

6 min de leitura
Observabilidade compartilhada: uma stack Grafana para todos os meus projetos

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:

  1. Consumo de recursos e custos operacionais – Quanto RAM/CPU a solução demanda? Quanto impacta a infraestrutura?
  2. Escalabilidade e manutenção – Como facilitar upgrades, backups e monitoramento geral?
  3. Unificação dos dados para correlação – É possível correlacionar traces, logs e métricas entre projetos?
  4. Complexidade na aplicação – A aplicação precisa hospedar algo além do seu domínio ou apenas emitir dados?
  5. 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:


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.

Blog do Felipe Marciano

Decisões de arquitetura, .NET e sistemas distribuídos, escritas por quem vive com as consequências delas.

© 2026 Felipe Marciano. Todos os direitos reservados.