<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Milton Camara</title>
    <description>The latest articles on DEV Community by Milton Camara (@lostdeveloper).</description>
    <link>https://dev.to/lostdeveloper</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2701171%2F46b66528-4d4f-445e-a549-fc9568c99d77.png</url>
      <title>DEV Community: Milton Camara</title>
      <link>https://dev.to/lostdeveloper</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lostdeveloper"/>
    <language>en</language>
    <item>
      <title>Pare de usar Logs como MULETA. Voce está fazendo ERRADO!!!</title>
      <dc:creator>Milton Camara</dc:creator>
      <pubDate>Mon, 11 May 2026 17:27:51 +0000</pubDate>
      <link>https://dev.to/lostdeveloper/pare-de-usar-logs-como-muleta-voce-esta-fazendo-errado-369e</link>
      <guid>https://dev.to/lostdeveloper/pare-de-usar-logs-como-muleta-voce-esta-fazendo-errado-369e</guid>
      <description>&lt;p&gt;Se você já abriu um arquivo de log de 2GB no meio da madrugada tentando descobrir por que o pagamento do cliente falhou, esse post é pra você. E não, a culpa não é da ferramenta. A culpa é sua.&lt;/p&gt;

&lt;p&gt;A real é dura: &lt;strong&gt;a maioria dos devs trata log como se fosse &lt;code&gt;Console.WriteLine&lt;/code&gt; com esteroides.&lt;/strong&gt; Espalha &lt;code&gt;LogInformation&lt;/code&gt; em cada método, acha que está "instrumentando" a aplicação, e quando o sistema quebra em produção fica horas "grepando" arquivo atrás de pista.&lt;/p&gt;

&lt;p&gt;Logs não foram feitos pra explicar fluxos. Eles servem pra registrar eventos pontuais. Quem te entrega o &lt;strong&gt;fluxo completo&lt;/strong&gt; de uma operação são os &lt;strong&gt;traces&lt;/strong&gt;. E se você ainda não sabe a diferença, esse é o problema que você precisa resolver hoje.&lt;/p&gt;




&lt;h2&gt;
  
  
  O pecado capital: log como narrador de novela
&lt;/h2&gt;

&lt;p&gt;Olha se você nunca escreveu algo parecido com isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entrou no método ProcessarPagamento"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Validando dados do cliente {ClienteId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clienteId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Chamando API de Pagamento com valor {Valor}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pagamento retornou sucesso"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Saiu do método ProcessarPagamento"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parece responsável. Parece "boa prática". &lt;strong&gt;Não é.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esse código tem 4 problemas graves que ninguém te conta:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Zero correlação entre as linhas
&lt;/h3&gt;

&lt;p&gt;Em produção, com 500 requests por segundo, essas 5 linhas vão estar &lt;strong&gt;embaralhadas com outras 50.000&lt;/strong&gt; de outros usuários. Boa sorte tentando montar a sequência de um pedido específico sem um &lt;code&gt;TraceId&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Você está pagando caro por lixo
&lt;/h3&gt;

&lt;p&gt;Datadog, New Relic, CloudWatch, Splunk: todos cobram por &lt;strong&gt;volume ingerido&lt;/strong&gt;. Cada &lt;code&gt;LogInformation("Entrou no método X")&lt;/code&gt; é dinheiro real saindo da sua conta. Já vi empresa gastar R$ 80k/mês em log que ninguém nunca leu.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. PII e vazamento de dados (LGPD chegou em todo mundo)
&lt;/h3&gt;

&lt;p&gt;Quando o dev coloca &lt;code&gt;logger.LogInformation("Request: {@Request}", request)&lt;/code&gt; achando que está sendo esperto, ele acabou de &lt;strong&gt;logar CPF, cartão, token JWT e senha&lt;/strong&gt; em texto puro. Auditoria vai te encontrar. A LGPD também.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Logs não contam história, contam fofoca
&lt;/h3&gt;

&lt;p&gt;Você sabe &lt;strong&gt;que&lt;/strong&gt; algo aconteceu, mas não sabe &lt;strong&gt;quando&lt;/strong&gt;, &lt;strong&gt;quanto tempo demorou&lt;/strong&gt;, &lt;strong&gt;o que veio antes&lt;/strong&gt; nem &lt;strong&gt;o que veio depois&lt;/strong&gt;. É como tentar entender um filme só pelas legendas, fora de ordem.&lt;/p&gt;




&lt;h2&gt;
  
  
  A virada de chave: traces
&lt;/h2&gt;

&lt;p&gt;Trace é uma &lt;strong&gt;árvore&lt;/strong&gt;. Cada operação (uma chamada HTTP, um query no banco, uma publicação no Kafka) é um &lt;strong&gt;span&lt;/strong&gt;, e os spans se conectam formando o fluxo completo de uma requisição, mesmo quando ela atravessa 8 microsserviços diferentes.&lt;/p&gt;

&lt;p&gt;Com trace você responde perguntas que log nunca vai conseguir responder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Por que esse pedido demorou 4 segundos?" → você vê &lt;strong&gt;exatamente&lt;/strong&gt; qual span travou.&lt;/li&gt;
&lt;li&gt;"O bug está no meu serviço ou no fornecedor?" → o trace mostra a cadeia inteira.&lt;/li&gt;
&lt;li&gt;"Quantos % das requests estão lentas por causa do banco?" → métricas derivadas do span do EF Core.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E o melhor: você instrumenta &lt;strong&gt;uma vez&lt;/strong&gt;, com OpenTelemetry, e exporta pra qualquer backend (Jaeger, Tempo, Datadog, Honeycomb, Azure Monitor). &lt;strong&gt;Sem vendor lock-in.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Hands-on: o mesmo cenário, agora bem feito (.NET 8 + OpenTelemetry)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup mínimo no &lt;code&gt;Program.cs&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;OpenTelemetry.Resources&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;OpenTelemetry.Trace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenTelemetry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MinhaApp.Pagamento"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tracing&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tracing&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MinhaApp.Pagamento"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAspNetCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpClientInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEntityFrameworkCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOtlpExporter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// exporta pro coletor que você quiser&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto. Toda chamada HTTP de entrada, toda chamada HTTP de saída e toda query no EF Core &lt;strong&gt;já estão sendo rastreadas automaticamente&lt;/strong&gt;. Você nem precisa escrever código pra isso.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instrumentação manual onde importa
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Diagnostics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Telemetry&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ActivitySource&lt;/span&gt; &lt;span class="n"&gt;Source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MinhaApp.Pagamento"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagamentoService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PagamentoService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PagamentoService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PagamentoService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_http&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ProcessarAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;pedidoId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;clienteId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;activity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Telemetry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"processar-pagamento"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;ActivityKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Internal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// tags = metadados consultáveis no backend de observabilidade&lt;/span&gt;
        &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pedido.id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pedidoId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cliente.id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clienteId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"valor.total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"moeda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"BRL"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;AddEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ActivityEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"validando-cliente"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ValidarClienteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clienteId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;AddEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ActivityEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"chamando-gateway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ActivityTagsCollection&lt;/span&gt; 
                &lt;span class="p"&gt;{&lt;/span&gt; 
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"gateway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Stripe"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"endpoint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"POST /v1/charges"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}));&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PostAsJsonAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/v1/charges"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http.status_code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"response.size"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActivityStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Gateway recusou pagamento"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="c1"&gt;// log SÓ aqui, no erro, e SEM payload sensível&lt;/span&gt;
                &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pagamento recusado para pedido {PedidoId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pedidoId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActivityStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;RecordException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActivityStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// deixa o ASP.NET Core lidar&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  O que esse código faz que o anterior não fazia
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Antes (logs)&lt;/th&gt;
&lt;th&gt;Depois (traces)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5 linhas soltas no arquivo&lt;/td&gt;
&lt;td&gt;1 span estruturado com início, fim e duração&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nenhuma correlação entre serviços&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;TraceId&lt;/code&gt; propaga automaticamente via HTTP headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Busca textual com &lt;code&gt;grep&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Query estruturada por &lt;code&gt;pedido.id&lt;/code&gt;, &lt;code&gt;cliente.id&lt;/code&gt;, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sem noção de tempo&lt;/td&gt;
&lt;td&gt;Latência precisa de cada etapa, em microssegundos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logger genérico vazando PII&lt;/td&gt;
&lt;td&gt;Tags controladas, sem campos sensíveis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custo proporcional ao volume bruto&lt;/td&gt;
&lt;td&gt;Custo proporcional a operações + sampling configurável&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  "E os logs, jogo fora?"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Não.&lt;/strong&gt; Logs continuam sendo úteis. O que muda é &lt;strong&gt;quando&lt;/strong&gt; você os usa:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Use log pra:&lt;/strong&gt; eventos de negócio importantes (&lt;code&gt;"Pedido X cancelado pelo cliente"&lt;/code&gt;), erros inesperados, auditoria de ações sensíveis.&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Não use log pra:&lt;/strong&gt; rastrear fluxo, medir performance, debugar requisição específica, "marcar que passou aqui".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A regra que eu sigo: &lt;strong&gt;se a informação faz parte da história de uma requisição, é trace. Se é um fato isolado que importa fora do contexto da requisição, é log.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;E quando você precisar dos dois juntos? Correlacione pelo &lt;code&gt;TraceId&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"Pedido {PedidoId} cancelado. TraceId: {TraceId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;pedidoId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;TraceId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, do dashboard de logs, você clica no &lt;code&gt;TraceId&lt;/code&gt; e cai direto no trace. &lt;strong&gt;Fim do &lt;code&gt;grep&lt;/code&gt; eterno.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A pirâmide da observabilidade (decora isso)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        🔺 Logs        ← eventos pontuais, raros, com contexto
       🔺🔺 Métricas    ← agregações, dashboards, alertas
      🔺🔺🔺 Traces      ← a base de tudo, o fluxo das requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quem começa pela base &lt;strong&gt;debuga em segundos&lt;/strong&gt;. Quem começa pelo topo &lt;strong&gt;debuga em horas&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusão sem rodeios
&lt;/h2&gt;

&lt;p&gt;Se você ainda está enchendo seu código de &lt;code&gt;LogInformation&lt;/code&gt; achando que está fazendo observabilidade, você está &lt;strong&gt;uma década atrás&lt;/strong&gt; do estado da arte. Trace não é luxo de Big Tech, é o &lt;strong&gt;mínimo&lt;/strong&gt; pra qualquer aplicação séria em 2026.&lt;/p&gt;

&lt;p&gt;Instala o OpenTelemetry hoje. Roda um Jaeger ou Tempo local. Vê seu sistema do jeito que ele realmente é: uma árvore de operações com latências, erros e dependências. Você nunca mais vai voltar pro &lt;code&gt;tail -f&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E aí, vai continuar caçando agulha no palheiro ou vai aprender a usar a ferramenta certa?&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;💬 Se esse post fez sentido, deixa um comentário com o pior cenário de "debug por log" que você já viveu.&lt;/p&gt;

</description>
      <category>logs</category>
      <category>traces</category>
      <category>debug</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>🔍 Como tornei milhões de conversas históricas "invisíveis" buscáveis em tempo real</title>
      <dc:creator>Milton Camara</dc:creator>
      <pubDate>Mon, 11 May 2026 14:40:00 +0000</pubDate>
      <link>https://dev.to/lostdeveloper/como-tornei-milhoes-de-conversas-historicas-invisiveis-buscaveis-em-tempo-real-d15</link>
      <guid>https://dev.to/lostdeveloper/como-tornei-milhoes-de-conversas-historicas-invisiveis-buscaveis-em-tempo-real-d15</guid>
      <description>&lt;h2&gt;
  
  
  O problema
&lt;/h2&gt;

&lt;p&gt;Trabalhei recentemente em uma solução para um cenário que muita gente que &lt;br&gt;
mexe com sistemas em escala já enfrentou: um sistema gera diariamente &lt;br&gt;
arquivos JSON que vão para um storage ou qualquer outro banco como histórico, seguindo um padrão de nomenclatura previsível. Com o tempo, esses arquivos se acumulam aos milhões.&lt;/p&gt;

&lt;p&gt;A dor apareceu quando surgiu a necessidade de consultar esse histórico. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Três problemas surgiram ao mesmo tempo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Parte da informação que identifica cada arquivo nem sempre estava preenchida no nome, porque é um dado mutável e nem sempre estava disponível no momento da geração.&lt;/p&gt;

&lt;p&gt;As pessoas precisavam buscar por critérios variados, alguns estruturados, outros por palavra-chave dentro do conteúdo, e nada disso estava exposto no nome do arquivo&lt;/p&gt;

&lt;p&gt;Eram milhões de arquivos espalhados em containers diferentes, varrer linearmente era inviável. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Em resumo: tínhamos os dados, mas eram efetivamente invisíveis.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução
&lt;/h2&gt;

&lt;p&gt;Construí um pipeline em .NET 9 que varre o storage e indexa todo o conteúdo em um cluster Elasticsearch (rodando em Kubernetes via ECK &lt;br&gt;
Operator), com mapping otimizado para texto em português (analyzer com &lt;br&gt;
stemmer, asciifolding e stopwords em PT-BR). O resultado é busca textual &lt;br&gt;
em milhões de documentos em tempo real, mesmo quando o termo aparece &lt;br&gt;
apenas no meio de uma seção específica do conteúdo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Os pontos de design que fizeram diferença&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pipeline producer/consumer com System.Threading.Channels&lt;/strong&gt; &lt;br&gt;
Producer lista arquivos paginado, workers em paralelo baixam e parseiam em streaming, e bulk indexer envia batches pro Elastic. Channels bounded controlam pressão de memória.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parser tolerante a dados sujos&lt;/strong&gt;&lt;br&gt;
Os arquivos históricos vinham com toda sorte de inconsistência: HTML entities, campos ausentes, datas inválidas, números como string, ordem de seções variável. Implementei discriminated union ParseResult (Success/Skipped/Failed) com helpers TryGet defensivos. &lt;br&gt;
Política: indexa o que conseguiu, descarta apenas o irrecuperável, nunca derruba o pipeline por um arquivo ruim.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Busca híbrida em duas camadas&lt;/strong&gt;&lt;br&gt;
Quando o termo digitado tem um padrão estruturado, a API consulta o próprio storage (prefix lookup nativo, extremamente rápido) e o Elastic em paralelo. Resultados unidos: hits do storage no topo, hits textuais abaixo. O melhor de dois mundos sem latência adicional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boosts diferenciados por campo no Elastic&lt;/strong&gt;&lt;br&gt;
Match em campos estruturados tem boost maior. Match em texto solto tem boost menor. Resultado: busca por uma palavra específica sobe primeiro quem tem essa palavra em campo identificador, e só depois quem mencionou em texto livre.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Estratégia de alias para reindexação sem downtime&lt;/strong&gt;&lt;br&gt;
Cada carga gera um índice físico novo (com timestamp no nome) e o alias é trocado atomicamente no final. Permite reprocessar o histórico inteiro sem afetar produção.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkpointing stateless&lt;/strong&gt;&lt;br&gt;
Cada partição tem seu progresso salvo em storage externo. Se o pod morrer no meio da carga, retoma exatamente de onde parou. Sem volumes persistentes, sem estado local.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Highlights com fragmentos contextuais&lt;/strong&gt;&lt;br&gt;
Quando a busca casa em texto livre, o Elastic retorna trechos do conteúdo com a palavra encontrada destacada. A interface mostra o contexto direto na lista de resultados, sem precisar abrir cada item para descobrir por que ele apareceu.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 O papel da IA nesse processo
&lt;/h2&gt;

&lt;p&gt;Não vou fingir que cheguei nessa arquitetura sozinho em uma tarde. Usei &lt;br&gt;
LLM como par técnico ao longo de todo o caminho, e a forma como usei &lt;br&gt;
fez toda a diferença no resultado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Onde a IA ajudou de verdade&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brainstorming de trade-offs arquiteturais&lt;/strong&gt;&lt;br&gt;
"Mensageria entre processos faz sentido aqui ou é overhead?", "Cursor pagination ou from/size pra infinite scroll?", "Alias com swap ou reindex in-place?". &lt;br&gt;
Em vez de só pedir resposta, debati cada decisão e pesei prós e contras. A IA é excelente como sparring de design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Análise crítica do código existente&lt;/strong&gt;&lt;br&gt;
O projeto inicial tinha mais peças do que precisava. Pedi uma análise técnica completa, code smells, dead code, bugs latentes, aderência a boas práticas. Saiu um documento de diagnóstico priorizado por severidade. Decidi simplificar a arquitetura com base nesse diagnóstico.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modelagem do parser tolerante&lt;/strong&gt;&lt;br&gt;
Os cenários de dados sujos foram enumerados de forma exaustiva e cada um virou um caso de teste antes da implementação. Test-driven, mas com IA gerando o checklist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mapping do Elasticsearch&lt;/strong&gt;&lt;br&gt;
Definir analyzer correto pra português, decidir entre tipos de campo, projetar nested vs flat, é o tipo de decisão onde experiência prévia conta muito. Conversar com a IA acelerou em horas o que levaria dias de leitura de docs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompts estruturados para agente de codificação&lt;/strong&gt; &lt;br&gt;
Quebrei a implementação em fases, cada uma com prompt detalhado contendo arquitetura alvo, decisões já tomadas, restrições, e ordem de execução. Resultado: o agente executou sem perder contexto e sem inventar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Onde a IA NÃO substituiu o engenheiro&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decisões de negócio (volumes reais, padrões de acesso, regras de tratamento de exceções específicas do domínio)&lt;/li&gt;
&lt;li&gt;Validar suposições contra a realidade do projeto (rodar build, ler logs, executar queries pra verificar comportamento)&lt;/li&gt;
&lt;li&gt;Conhecer a infra real (capacidade dos volumes, classes de storage disponíveis, limites do cluster)&lt;/li&gt;
&lt;li&gt;Priorizar segurança (a IA aponta riscos, mas a decisão de parar tudo pra resolver é humana)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A lição mais importante&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;IA é multiplicador, não substituto. Quem souber fazer perguntas certas, validar respostas e contextualizar com a realidade do projeto extrai muito valor. Quem só pede "me dá a solução" recebe código genérico que não resolve o problema real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack utilizada
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;.NET 9 (Worker Service + ASP.NET Core)&lt;/li&gt;
&lt;li&gt;Azure Blob Storage (origem dos dados)&lt;/li&gt;
&lt;li&gt;Elasticsearch 8.x em Kubernetes via ECK Operator&lt;/li&gt;
&lt;li&gt;Azure Table Storage (checkpoint stateless)&lt;/li&gt;
&lt;li&gt;SDK oficial do Elastic (tipado)&lt;/li&gt;
&lt;li&gt;System.Threading.Channels (pipeline assíncrono in-process)&lt;/li&gt;
&lt;li&gt;Claude (sparring técnico e análise de código)&lt;/li&gt;
&lt;li&gt;Claude Code (execução guiada por prompts estruturados)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lições que valeram o post
&lt;/h2&gt;

&lt;p&gt;Dados reais são sujos. Investi mais tempo projetando tolerância a inconsistências do que no pipeline em si. E foi o tempo mais bem gasto, em produção, um único arquivo malformado não pode derrubar uma carga em larga escala.&lt;/p&gt;

&lt;p&gt;Mensageria nem sempre é a resposta. Para uma carga histórica única + ingestão futura via eventos do próprio storage, removi a camada de filas intermediária e simplifiquei para um indexer mais direto. Menos infra, menos custo, mais simples de testar.&lt;/p&gt;

&lt;p&gt;Storage tem capacidades subutilizadas. O prefix lookup nativo do storage é absurdamente rápido e barato. Combinar isso com Elastic para o resto deu uma estratégia híbrida que nenhum dos dois sozinho daria.&lt;/p&gt;

&lt;p&gt;Reindexar sem downtime é decisão arquitetural, não otimização. Resolver isso com alias desde o dia 1 me poupou de um problema futuro que apareceria do nada.&lt;/p&gt;

&lt;p&gt;IA não pensa por você, mas amplifica quem pensa. O diferencial está em fazer as perguntas certas, contextualizar com seu projeto e validar tudo na prática.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>elasticsearch</category>
    </item>
  </channel>
</rss>
