> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-parallel-read-in-order-multi-part.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Demonstração do HackerNews Analyzer

> Instrumente um app Node.js com um agente e envie logs, traces, métricas e replay de sessão ao ClickStack

export const AgentPrompt = ({prompt, title = "Configuração Assistida por Agente", description, outline, outlineLabel = "O que o agente fará", repositoryUrl, repositoryLabel = "ClickHouse/agent-skills"}) => {
  const [copied, setCopied] = useState(false);
  const handleCopy = async () => {
    const copyWithTextArea = () => {
      const textArea = document.createElement("textarea");
      textArea.value = prompt;
      textArea.style.position = "fixed";
      textArea.style.opacity = "0";
      document.body.appendChild(textArea);
      textArea.select();
      document.execCommand("copy");
      document.body.removeChild(textArea);
    };
    try {
      if (navigator?.clipboard?.writeText) {
        try {
          await navigator.clipboard.writeText(prompt);
        } catch {
          copyWithTextArea();
        }
      } else {
        copyWithTextArea();
      }
      setCopied(true);
      window.setTimeout(() => setCopied(false), 2000);
    } catch {}
  };
  return <div className="ch-agent-prompt-wrapper" data-mdast="ignore">
      <div className="ch-agent-prompt-main-row">
        <div className="ch-agent-prompt-left">
          <span className="ch-agent-prompt-title">{title}</span>
        </div>
        <div className="ch-agent-prompt-prompt-area" style={{
    overflow: "hidden"
  }}>
          <code className="ch-agent-prompt-prompt-text" style={{
    overflowX: "auto"
  }}>
            {prompt}
          </code>
        </div>
        <button type="button" className="ch-agent-prompt-copy-button" style={{
    boxSizing: "border-box",
    justifyContent: "center",
    minWidth: "8.25rem",
    whiteSpace: "nowrap"
  }} onClick={handleCopy} aria-label={copied ? "Copiado" : "Copiar prompt"}>
          {copied ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <polyline points="20 6 9 17 4 12" />
            </svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
              <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
            </svg>}
          <span style={{
    display: "grid",
    justifyItems: "center"
  }}>
            <span style={{
    gridArea: "1 / 1",
    visibility: copied ? "hidden" : "visible"
  }}>Copiar Prompt</span>
            <span style={{
    gridArea: "1 / 1",
    visibility: copied ? "visible" : "hidden"
  }}>Copiado</span>
          </span>
        </button>
      </div>
      {(description || repositoryUrl) && <div className="ch-agent-prompt-sub-row">
          {description && <span className="ch-agent-prompt-description">{description}</span>}
          {repositoryUrl && <a className="ch-agent-prompt-repository-link" href={repositoryUrl} target="_blank" rel="noopener noreferrer">
              {repositoryLabel}
            </a>}
        </div>}
      {outline?.length > 0 && <details className="ch-agent-prompt-outline">
          <summary className="ch-agent-prompt-outline-summary">
            <svg width="12" height="12" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" className="ch-agent-prompt-outline-chevron" aria-hidden="true">
              <path d="M6.1584 3.13508C6.35985 2.94621 6.67627 2.95642 6.86514 3.15788L10.6151 7.15788C10.7954 7.3502 10.7954 7.64949 10.6151 7.84182L6.86514 11.8418C6.67627 12.0433 6.35985 12.0535 6.1584 11.8646C5.95694 11.6757 5.94673 11.3593 6.1356 11.1579L9.565 7.49985L6.1356 3.84182C5.94673 3.64036 5.95694 3.32394 6.1584 3.13508Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd" />
            </svg>
            <span>{outlineLabel}</span>
          </summary>
          <ol className="ch-agent-prompt-outline-list">
            {outline.map((item, index) => <li key={index}>{item}</li>)}
          </ol>
        </details>}
    </div>;
};

<Note>
  **TL;DR**

  Clone o [HackerNews Analyzer](https://github.com/ClickHouse/hn-news-analyzer), preencha o `.env` com seu endpoint OTLP e token e cole o prompt do agente. O backend não precisa importar o OpenTelemetry; o agente integra `@hyperdx/node-opentelemetry` na inicialização do processo.

  Tempo necessário: cerca de 10 minutos
</Note>

O HackerNews Analyzer é um app Node.js que consulta o dataset HackerNews hospedado na demo pública do ClickHouse. Cada gráfico, tabela e caixa de busca corresponde a uma consulta real do ClickHouse. Portanto, cada interação produz um trace cujo span principal é a chamada HTTPS do backend ao ClickHouse.

Esta é uma tarefa diferente da [demo de replay de sessão](/pt-BR/clickstack/example-datasets/session-replay), que instrumenta um app executado apenas no navegador usando um ClickStack local em Docker. Aqui, você conta com instrumentação automática do backend, spans de consultas do ClickHouse e replay de sessão no mesmo app.

<h2 id="prerequisites">
  Pré-requisitos
</h2>

* Node 18+ e npm
* Um endpoint OTLP/HTTP do ClickStack e um token de ingestão:
  * **ClickHouse Cloud:** abra o serviço e acesse **ClickStack** → **Configure seu exporter do OpenTelemetry** → **Variáveis de ambiente**. O protocolo é `http/protobuf`. O header é `authorization=<ingestion token>`, sem o prefixo `Bearer`.
  * **Collector local:** use `http://localhost:4318`. Se o collector não estiver protegido, deixe `authorization=` em branco.

<h2 id="clone-the-repository">
  Clone o repositório
</h2>

Clone o [HackerNews Analyzer](https://github.com/ClickHouse/hn-news-analyzer), instale as dependências e copie o modelo de arquivo de ambiente:

```bash theme={null}
git clone https://github.com/ClickHouse/hn-news-analyzer.git
cd hn-news-analyzer
npm install
cp .env.example .env
```

Você preencherá o `.env` nas próximas etapas e, em seguida, instrumentará a aplicação a partir deste diretório.

<h2 id="instrument-the-application">
  Instrumente a aplicação
</h2>

<Steps>
  <Step title="Execute a aplicação" id="run-the-application">
    No diretório `hn-news-analyzer` clonado, inicie o aplicativo. A fonte de dados do ClickHouse usa, por padrão, o cluster de demonstração público e somente leitura, portanto é executado sem nenhuma configuração adicional:

    ```bash theme={null}
    ./run.sh
    ```

    Abra [http://localhost:5001](http://localhost:5001). Você verá um seletor de ano, estatísticas resumidas, um gráfico de atividade, tabelas com os principais usuários e domínios e uma caixa de busca. Explore: alterne entre os anos e aprofunde-se nas histórias.

    <Frame>
      <img src="https://mintcdn.com/private-7c7dfe99-parallel-read-in-order-multi-part/j4TqNPW6aWoq7zwy/images/clickstack/getting-started/hackernews_main.webp?fit=max&auto=format&n=j4TqNPW6aWoq7zwy&q=85&s=8893dcb341dbb8cffdf3d78821ce949c" alt="O aplicativo HackerNews Analyzer em execução localmente" width="2872" height="1474" data-path="images/clickstack/getting-started/hackernews_main.webp" />
    </Frame>

    Neste ponto, o aplicativo está em execução, mas ainda não está instrumentado. O ClickStack não exibe dados: está aguardando telemetria.
  </Step>

  <Step title="Configure o ambiente" id="configure-environment">
    Os SDKs leem as variáveis padrão do exporter do OpenTelemetry. Elas não estão codificadas diretamente no código-fonte. Abra o arquivo `.env` e defina:

    ```bash theme={null}
    OTEL_SERVICE_NAME=hn-analyzer-api
    OTEL_EXPORTER_OTLP_ENDPOINT=<your-otlp-http-endpoint>
    OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
    OTEL_EXPORTER_OTLP_HEADERS=authorization=<your-ingestion-token>
    OTEL_TRACES_EXPORTER=otlp
    OTEL_METRICS_EXPORTER=otlp
    OTEL_LOGS_EXPORTER=otlp
    ```

    `OTEL_EXPORTER_OTLP_ENDPOINT` é o endpoint OTLP/HTTP (porta `4318`). `OTEL_EXPORTER_OTLP_HEADERS` é o cabeçalho de autorização, no formato `authorization=<token>`, sem o prefixo `Bearer`.

    Se o collector não exigir autenticação, deixe o token vazio (`OTEL_EXPORTER_OTLP_HEADERS=authorization=`). A variável ainda deve estar presente; o SDK ignora a inicialização se ela não estiver definida ou estiver totalmente vazia.

    O SDK do navegador reutiliza esses mesmos valores. O `vite.config.ts` incorpora o endpoint e o token ao pacote público durante a compilação; portanto, use um token de ingestão descartável, não um token de produção.
  </Step>

  <Step title="Instrumente a aplicação" id="instrument">
    Escolha um caminho. Todos os três levam ao mesmo aplicativo instrumentado.

    <Tabs>
      <Tab title="Instrumentação com agente" id="instrument-with-an-agent">
        Com o repositório clonado e o `.env` preenchido, cole este prompt em um agente de programação **nesse diretório** para instrumentar o aplicativo.

        <AgentPrompt
          prompt="Use curl para baixar, ler e seguir: github.com/ClickHouse/hn-news-analyzer/blob/main/agent.md"
          description="Depois de clonar hn-news-analyzer e preencher o .env, execute este prompt nesse diretório. Funciona com Claude Code, Cursor, Codex e outros agentes de programação."
          outline={[
"Confirme que você está no diretório clonado do hn-news-analyzer e que o .env já contém valores para OTEL_EXPORTER_OTLP_*. Interrompa se algum dos dois estiver ausente.",
"Instale @hyperdx/node-opentelemetry e altere run.sh para usar opentelemetry-instrument.",
"Instale @hyperdx/browser e habilite HyperDX.init e HyperDX.addAction.",
"Inicie o aplicativo, confirme que as verificações de saúde do OTLP passam e informe para navegar pelo aplicativo em http://localhost:5001.",
]}
        />
      </Tab>

      <Tab title="Instrumentação manual" id="instrument-manually">
        A instrumentação tem três partes: instalar os SDKs, alterar o comando de inicialização e habilitar o SDK do navegador. Nada disso altera a regra de negócio do aplicativo.

        <h3 id="install-node-sdk">
          Instale o SDK do Node
        </h3>

        ```bash theme={null}
        npm install @hyperdx/node-opentelemetry
        ```

        <h3 id="enable-run-sh-wrapper">
          Habilite o wrapper em run.sh
        </h3>

        No final de `run.sh`, há duas linhas `exec`. Comente a linha simples de `node` e descomente a instrumentada:

        ```diff theme={null}
         # ANTES: node simples, sem instrumentação:
        -exec node scripts/entrypoint.js
        +# exec node scripts/entrypoint.js

         # DEPOIS: mesmo código-fonte, envolvido por opentelemetry-instrument:
        -# exec npx opentelemetry-instrument scripts/entrypoint.js
        +exec npx opentelemetry-instrument scripts/entrypoint.js
        ```

        Continue iniciando por `scripts/entrypoint.js`. Esse shim chama `require('console')`, permitindo que a captura do console envolva `console.log`. Apontar `opentelemetry-instrument` diretamente para `dist/server/index.js` envia traces, mas descarta os logs silenciosamente.

        <h3 id="enable-browser-sdk">
          Habilite o SDK do navegador
        </h3>

        ```bash theme={null}
        npm install @hyperdx/browser
        ```

        Em `src/web/telemetry.ts`, descomente a importação, o bloco `HyperDX.init({...})` e `HyperDX.addAction` em `recordAction()`:

        ```diff theme={null}
        -// import HyperDX from '@hyperdx/browser';
        +import HyperDX from '@hyperdx/browser';

         export function initTelemetry(): void {
        -  // HyperDX.init({
        -  //   url: __OTLP_ENDPOINT__,
        -  //   apiKey: __OTLP_AUTH_TOKEN__,
        -  //   service: 'hn-analyzer-web',
        -  //   tracePropagationTargets: [/localhost:5001/i, /\/api\//i],
        -  //   consoleCapture: true,
        -  //   advancedNetworkCapture: true,
        -  // });
        +  HyperDX.init({
        +    url: __OTLP_ENDPOINT__,
        +    apiKey: __OTLP_AUTH_TOKEN__,
        +    service: 'hn-analyzer-web',
        +    tracePropagationTargets: [/localhost:5001/i, /\/api\//i],
        +    consoleCapture: true,
        +    advancedNetworkCapture: true,
        +  });
         }
        ```

        `__OTLP_ENDPOINT__` e `__OTLP_AUTH_TOKEN__` são constantes de tempo de compilação injetadas por `vite.config.ts` a partir dos mesmos valores `OTEL_EXPORTER_OTLP_*` usados pelo backend.

        <Warning>
          O token de ingestão é incorporado ao pacote público do navegador e pode ser lido por qualquer pessoa que inspecione a aba Network. Use um token descartável.
        </Warning>
      </Tab>

      <Tab title="Usar uma branch pré-instrumentada" id="use-the-instrumented-branch">
        Para pular a instrumentação e começar com um aplicativo já instrumentado, faça checkout da [branch `instrumented`](https://github.com/ClickHouse/hn-news-analyzer/tree/instrumented).

        ```bash theme={null}
        git checkout instrumented
        npm install
        ```

        Não execute `./reset.sh` nessa branch, a menos que queira remover os SDKs.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Gere tráfego e visualize a telemetria" id="generate-traffic-and-view-telemetry">
    Reinicie a aplicação para que o novo comando de inicialização e o bundle do navegador recém-gerado entrem em vigor:

    ```bash theme={null}
    # Ctrl-C the previous run, then:
    ./run.sh
    ```

    Confirme que o banner de inicialização exibe três linhas "Verificação de integridade aprovada" para `/v1/traces`, `/v1/metrics` e `/v1/logs`. Recarregue a aba do navegador para que o Vite sirva o pacote atualizado, depois alterne entre os anos e clique nas histórias para gerar tráfego.

    Abra a UI do ClickStack:

    1. Vá para **Busca** e filtre os últimos 5 minutos. Os logs de `hn-analyzer-api` começam a ser exibidos.

    <Frame>
      <img src="https://mintcdn.com/private-7c7dfe99-parallel-read-in-order-multi-part/j4TqNPW6aWoq7zwy/images/clickstack/getting-started/instrument_app_clickstack_logs.webp?fit=max&auto=format&n=j4TqNPW6aWoq7zwy&q=85&s=e93104dd5b9ee8d297a451510c1273b3" alt="Busca do ClickStack mostrando logs de hn-analyzer-api dos últimos cinco minutos" width="3018" height="1578" data-path="images/clickstack/getting-started/instrument_app_clickstack_logs.webp" />
    </Frame>

    2. Clique em uma solicitação e percorra o trace até a origem. Você verá o span do handler do Express, um span HTTP filho apontando para `sql-clickhouse.clickhouse.com` com duração real de rede e registros `console.log` correlacionados no mesmo trace.

    <Frame>
      <img src="https://mintcdn.com/private-7c7dfe99-parallel-read-in-order-multi-part/j4TqNPW6aWoq7zwy/images/clickstack/getting-started/instrument_app_clickstack_traces.webp?fit=max&auto=format&n=j4TqNPW6aWoq7zwy&q=85&s=a421c9aebd0d2e5966c5193f70c89667" alt="Trace do ClickStack com um span de handler do Express e um span HTTP filho para o ClickHouse" width="2398" height="1590" data-path="images/clickstack/getting-started/instrument_app_clickstack_traces.webp" />
    </Frame>

    3. Abra **Reprodução de sessão** para reproduzir um vídeo de uma sessão do navegador que permite avançar e retroceder, sincronizado com a linha do tempo do trace.

    <Frame>
      <img src="https://mintcdn.com/private-7c7dfe99-parallel-read-in-order-multi-part/j4TqNPW6aWoq7zwy/images/clickstack/getting-started/instrument_app_clickstack_sessions.webp?fit=max&auto=format&n=j4TqNPW6aWoq7zwy&q=85&s=6d9af2c26c59ee42f2df33f40fd40f62" alt="Reprodução de sessão do ClickStack sincronizada com a linha do tempo do trace" width="2408" height="1580" data-path="images/clickstack/getting-started/instrument_app_clickstack_sessions.webp" />
    </Frame>

    Logs, métricas, traces e reproduções de sessão são exibidos na mesma UI, compartilham a mesma linguagem de consulta e são correlacionados automaticamente.
  </Step>
</Steps>

<h2 id="learn-more">
  Saiba mais
</h2>

* [HackerNews Analyzer](https://github.com/ClickHouse/hn-news-analyzer): o repositório de demonstração instrumentado por este guia.

- [replay de sessão](/pt-BR/clickstack/features/session-replay): visão geral da funcionalidade, opções de SDK e controles de privacidade.
- [replay de sessão Demo](/pt-BR/clickstack/example-datasets/session-replay): uma demonstração completa com uma instância local do ClickStack.
- [ClickStack Primeiros passos](/pt-BR/clickstack/getting-started/index): implante o ClickStack e faça a ingestão dos seus primeiros dados.
- [Todos os datasets de exemplo](/pt-BR/clickstack/example-datasets/index): outros datasets de exemplo e guias.
