> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Instrument an application in 5 mins with Managed ClickStack

> Instrument a Node.js application with OpenTelemetry and send its logs, metrics, and traces into Managed ClickStack

This guide walks you through instrumenting a small Node.js application with OpenTelemetry and sending its logs, metrics, traces, and session replays into Managed ClickStack. The backend is instrumented with no changes to the application source code.

The [HackerNews Analyzer](https://github.com/ClickHouse/hn-news-analyzer) is a Node.js app that queries the HackerNews dataset hosted in the public ClickHouse demo. Every chart, table, and search box is backed by a real ClickHouse query, so every interaction produces a trace whose main span is the HTTPS call from the backend out to ClickHouse.

This guide assumes you've completed [Setting up your OpenTelemetry Collector](https://clickhouse.com/docs/clickstack/managed-onboarding/setting-up-your-opentelemetry-collector) and have a ClickStack collector running and reachable from the machine you run this application on. **Ensure you have recorded its OTLP endpoint** and the `OTLP_AUTH_TOKEN` you set when deploying it. You will put those values in `.env` before instrumenting.

## Prerequisites

* A ClickStack collector reachable from this machine. If you haven't deployed one yet, follow [Setting up your OpenTelemetry Collector](https://clickhouse.com/docs/clickstack/managed-onboarding/setting-up-your-opentelemetry-collector) first.
* The OTLP/HTTP endpoint of that collector (commonly port `4318`) and the `OTLP_AUTH_TOKEN` you set on it. Use these as `OTEL_EXPORTER_OTLP_ENDPOINT` and `OTEL_EXPORTER_OTLP_HEADERS=authorization=<token>`.
* Node 18+ and npm.

## Clone the repository

Clone [HackerNews Analyzer](https://github.com/ClickHouse/hn-news-analyzer), install dependencies, and copy the env template:

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

You'll fill `.env` in the next steps, then instrument from this directory.

## Instrument the application

**1. Run the application**

From the cloned `hn-news-analyzer` directory, start the app. The ClickHouse data source defaults to the public read-only demo cluster, so it runs without any further configuration:

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

Open [http://localhost:5001](http://localhost:5001). You will see a year selector, summary statistics, an activity chart, top users and domains tables, and a search box. Click around: switch years, drill into stories.

![The HackerNews Analyzer application running locally](https://mintcdn.com/private-7c7dfe99/LCIbqddLfKHZZnfk/images/clickstack/getting-started/hackernews_main.webp?fit=max\&auto=format\&n=LCIbqddLfKHZZnfk\&q=85\&s=5587ba7b6d7c6595e7782e5060586629)

At this point the application is running but uninstrumented. ClickStack shows no data: it is waiting for telemetry.

**2. Configure environment**

The SDKs read standard OpenTelemetry exporter variables. They are not hardcoded in source. Open `.env` and set:

```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` is the OTLP/HTTP endpoint (port `4318`). `OTEL_EXPORTER_OTLP_HEADERS` is the authorization header, in the form `authorization=<token>` with no `Bearer` prefix.

If the collector does not enforce auth, leave the token empty (`OTEL_EXPORTER_OTLP_HEADERS=authorization=`). The variable must still be present; the SDK skips initialization if it is unset or fully empty.

The browser SDK reuses these same values. `vite.config.ts` bakes the endpoint and token into the public bundle at build time, so use a throwaway ingestion token, not a production one.

**3. Instrument the application**

Pick one path. All three end at the same instrumented app.

> [tab] Agent instrumentation
>
> With the repo cloned and `.env` filled in, paste this prompt into a coding agent **in that directory** to instrument the application.
>
> Agent-Assisted Setup
>
> `Use curl to download, read and follow: github.com/ClickHouse/hn-news-analyzer/blob/main/agent.md`
>
> Copy Prompt
>
> Copied
>
> After you clone hn-news-analyzer and fill .env, run this prompt from that directory. Works with Claude Code, Cursor, Codex, and other coding agents.

> [tab] Manual instrumentation
>
> Instrumentation has three parts: install the SDKs, switch the launch command, and enable the browser SDK. None of it changes the application's business logic.
>
> ### Install the Node SDK
>
> ```bash theme={null}
> npm install @hyperdx/node-opentelemetry
> ```
>
> ### Enable the wrapper in run.sh
>
> The bottom of `run.sh` has two `exec` lines. Comment the plain `node` line and uncomment the instrumented one:
>
> ```diff theme={null}
>  # BEFORE: plain node, no instrumentation:
> -exec node scripts/entrypoint.js
> +# exec node scripts/entrypoint.js
>
>  # AFTER: same source, wrapped by opentelemetry-instrument:
> -# exec npx opentelemetry-instrument scripts/entrypoint.js
> +exec npx opentelemetry-instrument scripts/entrypoint.js
> ```
>
> Keep launching through `scripts/entrypoint.js`. That shim calls `require('console')` so console capture wraps `console.log`. Pointing `opentelemetry-instrument` at `dist/server/index.js` directly ships traces but silently drops logs.
>
> ### Enable the browser SDK
>
> ```bash theme={null}
> npm install @hyperdx/browser
> ```
>
> In `src/web/telemetry.ts`, uncomment the import, the `HyperDX.init({...})` block, and `HyperDX.addAction` in `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__` and `__OTLP_AUTH_TOKEN__` are compile-time constants injected by `vite.config.ts` from the same `OTEL_EXPORTER_OTLP_*` values the backend uses.
>
> > [!WARNING]
> >
> > The ingestion token is baked into the public browser bundle and is readable by anyone inspecting the network tab. Use a throwaway token.

> [tab] Use a pre-instrumented branch
>
> To skip instrumentation and start with an already instrumented application, check out the [`instrumented` branch](https://github.com/ClickHouse/hn-news-analyzer/tree/instrumented).
>
> ```bash theme={null}
> git checkout instrumented
> npm install
> ```
>
> Don't run `./reset.sh` on this branch unless you want to strip the SDKs.

**4. Generate traffic and view telemetry**

Restart the application so the new launch command and freshly built browser bundle take effect:

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

Confirm the startup banner prints three "Health check passed" lines for `/v1/traces`, `/v1/metrics`, and `/v1/logs`. Reload the browser tab so Vite serves the updated bundle, then switch years and click into stories to generate traffic.

Open the ClickStack UI:

1. Go to **Search** and filter to the last 5 minutes. Logs for `hn-analyzer-api` stream in.

![ClickStack search showing hn-analyzer-api logs from the last five minutes](https://mintcdn.com/private-7c7dfe99/LCIbqddLfKHZZnfk/images/clickstack/getting-started/instrument_app_clickstack_logs.webp?fit=max\&auto=format\&n=LCIbqddLfKHZZnfk\&q=85\&s=5fb0cdc33e97610f1f7f1c264f349aa0)

2. Click into a request and walk up the trace. You will see the Express handler span, a child HTTP span pointing at `sql-clickhouse.clickhouse.com` with real network duration, and correlated `console.log` records on the same trace.

![ClickStack trace with an Express handler span and a child HTTP span to ClickHouse](https://mintcdn.com/private-7c7dfe99/LCIbqddLfKHZZnfk/images/clickstack/getting-started/instrument_app_clickstack_traces.webp?fit=max\&auto=format\&n=LCIbqddLfKHZZnfk\&q=85\&s=77cefc89163e1e45a8475dd815ff95bd)

3. Open **Session Replay** to play back a scrubbable video of a browser session, synced to the trace timeline.

![ClickStack session replay synced to the trace timeline](https://mintcdn.com/private-7c7dfe99/LCIbqddLfKHZZnfk/images/clickstack/getting-started/instrument_app_clickstack_sessions.webp?fit=max\&auto=format\&n=LCIbqddLfKHZZnfk\&q=85\&s=c80327d0ed8a5ac088bfa5979febf452)

Logs, metrics, traces, and session replays land in the same UI, share the same query language, and are correlated automatically.

## Further reading

* [Monitoring Kubernetes](https://clickhouse.com/docs/clickstack/managed-onboarding/monitoring-kubernetes): collect logs, infra metrics, and Kubernetes events from a cluster.
* [Monitoring AWS CloudWatch logs](https://clickhouse.com/docs/clickstack/managed-onboarding/monitoring-aws-cloudwatch-logs): forward CloudWatch logs via the OpenTelemetry CloudWatch receiver.
* [Session Replay](https://clickhouse.com/docs/clickstack/features/session-replay.md): feature overview, SDK options, and privacy controls.
* [Going to production](https://clickhouse.com/docs/clickstack/managing/production.md) for recommendations when going to production.
