> ## 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.

# Демо HackerNews Analyzer

> Добавьте в приложение Node.js агент и отправляйте в ClickStack журналы, трассировки, метрики и записи воспроизведения сеансов

export const AgentPrompt = ({prompt, title = "Настройка с помощью агента", description, outline, outlineLabel = "Что будет делать агент", 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 ? "Скопировано" : "Скопировать промпт"}>
          {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"
  }}>Скопировать промпт</span>
            <span style={{
    gridArea: "1 / 1",
    visibility: copied ? "visible" : "hidden"
  }}>Скопировано</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>
  **Кратко**

  Клонируйте [HackerNews Analyzer](https://github.com/ClickHouse/hn-news-analyzer), укажите в `.env` конечную точку OTLP и токен, затем вставьте промпт агента. Backend не требует импортов OpenTelemetry: агент подключает `@hyperdx/node-opentelemetry` при запуске процесса.

  Требуемое время: около 10 минут
</Note>

HackerNews Analyzer — приложение Node.js, которое выполняет запросы к датасету HackerNews, размещённому в публичной демоверсии ClickHouse. Каждая диаграмма, таблица и поле поиска выполняют реальный запрос к ClickHouse, поэтому при каждом взаимодействии создаётся трассировка, основной спан которой — HTTPS-вызов из backend в ClickHouse.

Это отличается от [демонстрации воспроизведения сеанса](/ru/clickstack/example-datasets/session-replay), где браузерное приложение инструментируется для работы с локальным ClickStack в Docker. Здесь одно приложение предоставляет автоматическую инструментацию backend, спаны запросов к ClickHouse и воспроизведение сеанса.

<h2 id="prerequisites">
  Предварительные требования
</h2>

* Node 18+ и npm
* Конечная точка ClickStack OTLP/HTTP и токен ингестии:
  * **ClickHouse Cloud:** откройте сервис, затем перейдите в **ClickStack** → **Configure your OpenTelemetry exporter** → **Env vars**. Протокол: `http/protobuf`. Заголовок: `authorization=<ingestion token>` без префикса `Bearer`.
  * **Локальный collector:** используйте `http://localhost:4318`. Если collector не защищён, оставьте `authorization=` пустым.

<h2 id="clone-the-repository">
  Клонируйте репозиторий
</h2>

Клонируйте [HackerNews Analyzer](https://github.com/ClickHouse/hn-news-analyzer), установите зависимости и скопируйте шаблон файла окружения:

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

На следующих шагах вы заполните `.env`, а затем из этого каталога инструментируете приложение.

<h2 id="instrument-the-application">
  Инструментируйте приложение
</h2>

<Steps>
  <Step title="Запустите приложение" id="run-the-application">
    В клонированном каталоге `hn-news-analyzer` запустите приложение. По умолчанию источником данных ClickHouse служит общедоступный демонстрационный кластер в режиме «только для чтения», поэтому дополнительная настройка не требуется:

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

    Откройте [http://localhost:5001](http://localhost:5001). Вы увидите переключатель года, сводную статистику, диаграмму активности, таблицы самых активных пользователей и доменов, а также поле поиска. Попробуйте приложение: переключайте годы, открывайте подробности историй.

    <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="Приложение HackerNews Analyzer, запущенное локально" width="2872" height="1474" data-path="images/clickstack/getting-started/hackernews_main.webp" />
    </Frame>

    На этом этапе приложение запущено, но не оснащено инструментированием. ClickStack пока не показывает данных: он ожидает телеметрию.
  </Step>

  <Step title="Настройте окружение" id="configure-environment">
    SDK используют стандартные переменные экспортёра OpenTelemetry. Они не прописаны жёстко в исходном коде. Откройте `.env` и задайте:

    ```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` — конечная точка OTLP/HTTP (порт `4318`). `OTEL_EXPORTER_OTLP_HEADERS` — заголовок авторизации в формате `authorization=<token>` без префикса `Bearer`.

    Если коллектор не требует аутентификации, оставьте токен пустым (`OTEL_EXPORTER_OTLP_HEADERS=authorization=`). Переменная всё равно должна быть задана: SDK пропускает инициализацию, если она не задана или полностью пуста.

    Browser SDK использует те же значения. `vite.config.ts` встраивает конечную точку и токен в публичный bundle на этапе сборки, поэтому используйте одноразовый токен ингестии, а не токен для продакшна.
  </Step>

  <Step title="Инструментируйте приложение" id="instrument">
    Выберите один из вариантов. Все три приведут к одному и тому же инструментированному приложению.

    <Tabs>
      <Tab title="Инструментирование с помощью агента" id="instrument-with-an-agent">
        Клонировав репозиторий и заполнив `.env`, вставьте этот промпт в coding agent **из этого каталога**, чтобы инструментировать приложение.

        <AgentPrompt
          prompt="С помощью curl скачайте, прочитайте и выполните инструкции из: github.com/ClickHouse/hn-news-analyzer/blob/main/agent.md"
          description="После клонирования hn-news-analyzer и заполнения .env запустите этот промпт из этого каталога. Работает с Claude Code, Cursor, Codex и другими coding agents."
          outline={[
"Убедитесь, что вы находитесь в клонированном каталоге hn-news-analyzer и что .env уже содержит значения OTEL_EXPORTER_OTLP_*. Если хотя бы одно из условий не выполнено, остановитесь.",
"Установите @hyperdx/node-opentelemetry и переключите run.sh на opentelemetry-instrument.",
"Установите @hyperdx/browser и включите HyperDX.init и HyperDX.addAction.",
"Запустите приложение, убедитесь, что проверки работоспособности OTLP проходят успешно, и предложите перейти по адресу http://localhost:5001.",
]}
        />
      </Tab>

      <Tab title="Ручное инструментирование" id="instrument-manually">
        Инструментирование состоит из трёх частей: установки SDK, изменения команды запуска и включения браузерного SDK. Это не изменяет бизнес-логику приложения.

        <h3 id="install-node-sdk">
          Установите Node SDK
        </h3>

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

        <h3 id="enable-run-sh-wrapper">
          Включите обёртку в run.sh
        </h3>

        В конце `run.sh` находятся две строки `exec`. Закомментируйте обычную строку `node` и раскомментируйте строку с инструментированием:

        ```diff theme={null}
         # ДО: обычный node, без инструментирования:
        -exec node scripts/entrypoint.js
        +# exec node scripts/entrypoint.js

         # ПОСЛЕ: тот же исходный код, обёрнутый opentelemetry-instrument:
        -# exec npx opentelemetry-instrument scripts/entrypoint.js
        +exec npx opentelemetry-instrument scripts/entrypoint.js
        ```

        Продолжайте запускать приложение через `scripts/entrypoint.js`. Этот shim вызывает `require('console')`, поэтому захват консольных сообщений оборачивает `console.log`. Если указать для `opentelemetry-instrument` непосредственно `dist/server/index.js`, трассы будут отправляться, но логи будут незаметно теряться.

        <h3 id="enable-browser-sdk">
          Включите браузерный SDK
        </h3>

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

        В `src/web/telemetry.ts` раскомментируйте импорт, блок `HyperDX.init({...})` и `HyperDX.addAction` в `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__` и `__OTLP_AUTH_TOKEN__` — константы времени компиляции, которые `vite.config.ts` внедряет из тех же значений `OTEL_EXPORTER_OTLP_*`, что использует backend.

        <Warning>
          Токен ингестии встраивается в публичный браузерный bundle и доступен любому, кто просматривает вкладку Network. Используйте одноразовый токен.
        </Warning>
      </Tab>

      <Tab title="Используйте предварительно инструментированную ветку" id="use-the-instrumented-branch">
        Чтобы пропустить инструментирование и начать работу с уже инструментированным приложением, переключитесь на [ветку `instrumented`](https://github.com/ClickHouse/hn-news-analyzer/tree/instrumented).

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

        Не запускайте `./reset.sh` в этой ветке, если не хотите удалить SDK.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Создайте трафик и просмотрите телеметрические данные" id="generate-traffic-and-view-telemetry">
    Перезапустите приложение, чтобы применились новая команда запуска и свежесобранная сборка для браузера:

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

    Убедитесь, что в стартовом баннере выводятся три строки "Проверка работоспособности пройдена" для `/v1/traces`, `/v1/metrics` и `/v1/logs`. Перезагрузите вкладку браузера, чтобы Vite отдал обновлённый bundle, затем переключайте годы и открывайте истории, чтобы сгенерировать трафик.

    Откройте интерфейс ClickStack:

    1. Перейдите в **Search** и установите период «последние 5 минут». Начнут поступать журналы `hn-analyzer-api`.

    <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="Поиск ClickStack с журналами hn-analyzer-api за последние пять минут" width="3018" height="1578" data-path="images/clickstack/getting-started/instrument_app_clickstack_logs.webp" />
    </Frame>

    2. Откройте запрос и перейдите к родительским спанам трассировки. Вы увидите спан обработчика Express, дочерний HTTP-спан, указывающий на `sql-clickhouse.clickhouse.com`, с фактической длительностью сетевого запроса, а также коррелированные записи `console.log` в той же трассировке.

    <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="Трассировка ClickStack со спаном обработчика Express и дочерним HTTP-спаном к ClickHouse" width="2398" height="1590" data-path="images/clickstack/getting-started/instrument_app_clickstack_traces.webp" />
    </Frame>

    3. Откройте **Воспроизведение сеанса**, чтобы просмотреть видео сеанса браузера с возможностью перемотки, синхронизированное со шкалой времени трассировки.

    <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="Воспроизведение сеанса ClickStack, синхронизированное со шкалой времени трассировки" width="2408" height="1580" data-path="images/clickstack/getting-started/instrument_app_clickstack_sessions.webp" />
    </Frame>

    Журналы, метрики, трассировки и воспроизведения сеансов отображаются в одном интерфейсе, используют один язык запросов и автоматически коррелируются.
  </Step>
</Steps>

<h2 id="learn-more">
  Подробнее
</h2>

* [HackerNews Analyzer](https://github.com/ClickHouse/hn-news-analyzer): репозиторий демо-приложения, которое инструментируется в этом руководстве.
* [Воспроизведение сеанса](/ru/clickstack/features/session-replay): обзор этой возможности, варианты SDK и настройки конфиденциальности.
* [Демо воспроизведения сеанса](/ru/clickstack/example-datasets/session-replay): автономное демо с локальным экземпляром ClickStack.
* [Начало работы с ClickStack](/ru/clickstack/getting-started/index): разверните ClickStack и настройте приём первых данных.
* [Все демонстрационные наборы данных](/ru/clickstack/example-datasets/index): другие демонстрационные наборы данных и руководства.
