HyperDX 2.41.0 常用 CLI 命令详解

HyperDX 2.41.0 常用 CLI 命令详解:以实测四服务栈为基准,覆盖 hdx CLI 0.7.0(认证、数据源发现、SQL 查询、终端图表、TUI)、Docker Compose 编排运维、otelcontribcol 配置校验三场景、外部 API v2 的 curl 调用(team/connections/sources/search/charts/series)与三路日志排查,全部命令与回显均为 2026-10-23 实测。

HyperDX可观测性自托管2026/10/23

HyperDX 2.41.0 常用 CLI 命令详解

目标:把 HyperDX 2.41.0(ClickStack 开源版)日常运维会用到的五类命令工具——hdx 终端 CLI、Docker Compose 栈编排、collector 配置校验、外部 REST API(curl)、日志排查与底层体检——逐条实测并沉淀参数与回显,读者照抄即可在自己的栈上复现同样的结果。本文全部命令与回显均在 2026-10-23 于 Linux 服务器实测执行,栈的部署过程见 《HyperDX 2.41.0 Docker Compose 部署指南》。

一、环境信息

实测环境(2026-10-23,真实服务器视角):

  • 栈:B2 篇部署的 HyperDX 2.41.0 四服务栈(app / otel-collector / ClickHouse 26.1.12.23 / MongoDB 5.0.32),项目名 hyperdx-2410-stack,四服务全部运行中
  • 服务端口(与本栈编排一致):UI 8130、API 8136、OTLP gRPC 8137、OTLP HTTP 8138、collector 健康检查 8139、fluentd receiver 8152
  • 示例账号:root@quzuo.net / Q1w2e3r4t5y6!(首用户,注册过程见 B2 篇第 4.4 节)
  • 团队 ingest key(OTLP 摄入用):cca923a9-201f-4aa6-9d2d-98d016261d61
  • 用户 accessKey(外部 API 用):b758ccd0-1eb5-4179-a365-82fdffe3e95d
  • CLI 运行环境:Node.js v24.21.0、npm 11.19.0(registry https://registry.npmmirror.com)、@hyperdx/cli 安装版本 0.7.0
  • 容器运行时:Docker 29.8.1、Docker Compose v5.5.1
$ hdx --version
0.7.0
$ docker --version
Docker version 29.8.1, build 4a63305
$ docker compose version
Docker Compose version v5.5.1

二、工具概览

ClickStack 没有单一的"万能 CLI",日常命令分散在五个入口,各自负责一层:

工具形态负责什么典型命令
hdx(@hyperdx/cli)终端 CLI / TUI以用户身份检索日志与追踪、跑 SQL、画终端图表、切团队hdx auth login、hdx query、hdx chart
Docker Compose栈编排四服务栈的启停、状态、日志、配置校验docker compose ps、docker compose logs
otelcontribcol validatecollector 内置校验器校验 OTel Collector 配置文件语法与组件装配validate --config=…
外部 API v2REST(curl)程序化检索与资源管理(搜索、图表序列、告警、仪表盘等)POST /api/v2/search
docker compose exec + 客户端底层体检直入 ClickHouse / MongoDB 查内部状态clickhouse-client、mongosh

关键差异与分工:hdx 走的是「用户会话 + HyperDX 查询代理」,与网页端看到的结果严格一致;curl + /api/v2 走「accessKey + REST」,用于脚本与外部系统集成;clickhouse-client / mongosh 则绕开应用层直查存储,排障时定位「数据到底落没落库」用。三层在本文都有实测。

三、前置条件

  1. 已按 B2 篇完成四服务栈部署且四服务全部运行(docker compose ps 确认);
  2. 已注册首用户并登录过一次(本文示例 root@quzuo.net);
  3. 宿主机已装 Node.js ≥ 22.16(hdx CLI 的硬性要求,本文实测 24.21.0);
  4. 已取得两个密钥(获取命令见第七章 7.1):团队 ingest key 与用户 accessKey;
  5. hdx CLI 的 --app-url 指向 UI 端口(本文 8130),而 curl 直调 API 用 API 端口(本文 8136)——两者别混,原因见第十章常见问题 1。

四、hdx CLI:终端检索与查询

hdx 是 HyperDX 官方终端客户端(npm 包 @hyperdx/cli),与网页端跑同一条查询管线,适合在 SSH 会话里直接检索事件、跑 SQL、看趋势。本章全部命令在实测栈上执行。

4.1 安装与认证(auth)

全局安装得到 hdx 可执行文件;认证用 auth login,支持交互与非交互两种方式:

$ npm install -g @hyperdx/cli
changed 1 package in 315ms
$ hdx --version
0.7.0

非交互登录(CI 友好),-a 指 UI 端口:

$ hdx auth login -a http://localhost:8130 -e root@quzuo.net -p 'Q1w2e3r4t5y6!'
Logged in as root@quzuo.net (http://localhost:8130)

认证状态与会话存储:

$ hdx auth status
Logged in as root@quzuo.net (http://localhost:8130)
Team: root@quzuo.net's Team [6adaae2ed640de28d174c2ad]
$ ls -la ~/.config/hyperdx/cli/
total 12
drwx------ 2 root root 4096 10月 23 08:47 .
drwx------ 3 root root 4096 10月 23 14:36 ..
-rw------- 1 root root  160 10月 23 14:36 session.json

会话文件权限 0600,所有子命令复用它直到过期;登出用 hdx auth logout(无额外参数)。

4.2 数据源与连接发现(sources / connections / dashboards / team)

写 SQL 或画图之前先弄清「有什么源、哪个连接、哪些仪表盘」。四个发现类命令都支持 --json:

$ hdx connections --json
[
  {
    "id": "6adaae2ed640de28d174c2b2",
    "name": "Local ClickHouse",
    "host": "http://ch-server:8123"
  }
]
$ hdx dashboards
No dashboards found.

hdx sources 列出全部数据源及其 ClickHouse 建表语句(可用作字段字典)。完整输出很长,以下为实测节选(首尾):

$ hdx sources
Logs  log  default.otel_logs
  CREATE TABLE default.otel_logs
  (
      `Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
      `TraceId` String CODEC(ZSTD(1)),
      `SpanId` String CODEC(ZSTD(1)),
      `SeverityText` LowCardinality(String) CODEC(ZSTD(1)),
      `ServiceName` LowCardinality(String) CODEC(ZSTD(1)),
      `Body` String CODEC(ZSTD(1)),
      `ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
      (中段为 Scope/LogAttributes 等列与 6 个 __hdx_materialized_k8s_* 物化列、8 个索引定义,此处略)
      INDEX idx_lower_body lower(Body) TYPE tokenbf_v1(32768, 3, 0) GRANULARITY 8
  )
  ENGINE = MergeTree
  PARTITION BY toDate(Timestamp)
  ORDER BY (toStartOfFiveMinutes(Timestamp), ServiceName, Timestamp)
  TTL toDateTime(Timestamp) + toIntervalDay(30)
  SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, enable_block_number_column = 1, enable_block_offset_column = 1

Traces  trace  default.otel_traces
  (otel_traces 建表语句,含 SpanAttributes/Duration/StatusCode 与 Links 三列数组等)

Metrics  metric  default.

Sessions  session  default.hyperdx_sessions
  CREATE TABLE default.hyperdx_sessions
  (
      `Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
      `TimestampTime` DateTime DEFAULT toDateTime(Timestamp),
      (中段列与 otel_logs 同构,此处略)
  )
  ENGINE = MergeTree
  PARTITION BY toDate(TimestampTime)
  PRIMARY KEY (ServiceName, TimestampTime)
  ORDER BY (ServiceName, TimestampTime, Timestamp)
  TTL TimestampTime + toIntervalDay(30)
  SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1

💡 注意两点实测细节:① Logs/Traces 源的时间列是 Timestamp,只有 Sessions 源额外有 TimestampTime 派生列(与 C1 篇 5.4 节的列名分布实测一致);② Metrics 源没有单一表名,按指标类型分散在 otel_metrics_gauge/histogram/sum 等表,所以 sources 输出里 Metrics 行的表名显示为 default.。

团队命令(单团队部署下基本是只读确认):

$ hdx team list
▸ root@quzuo.net's Team  [6adaae2ed640de28d174c2ad]

You belong to a single team. hdx team use is a no-op here.
$ hdx team current
root@quzuo.net's Team  [6adaae2ed640de28d174c2ad]

4.3 原始 SQL 查询(query)

hdx query 把 ClickHouse SQL 经 HyperDX 代理下推执行,输出默认 JSONEachRow(每行一个 JSON 对象),可直接进 jq:

$ hdx query --connection-id 6adaae2ed640de28d174c2b2 --sql "SELECT count() AS c FROM default.otel_logs"
{"c":"3"}
$ hdx query --connection-id 6adaae2ed640de28d174c2b2 --sql "SELECT ServiceName, count() AS c, max(Timestamp) AS latest FROM default.otel_logs GROUP BY ServiceName ORDER BY c DESC LIMIT 5"
{"ServiceName":"hyperdx-docs-test","c":"1","latest":"2026-10-23T00:46:03.531476487Z"}
{"ServiceName":"recreate-check","c":"1","latest":"2026-10-23T01:10:35.000000000Z"}
{"ServiceName":"compose-final-check","c":"1","latest":"2026-10-23T01:08:19.000000000Z"}

--connection-id 的取值来自 4.2 的 hdx connections --json。完整参数如下(hdx query --help 实测回显):

Usage: hdx query [options]

Run raw SQL against a ClickHouse connection (add --patterns to cluster result
into Drain log patterns)

Options:
  --connection-id <id>  Connection ID (from 'hdx connections --json')
  --sql <query>         SQL query to execute
  -a, --app-url <url>   HyperDX app URL
  --format <format>     ClickHouse output format (default: "JSONEachRow")
  --patterns            Mine Drain log patterns from the query result instead
                        of emitting rows
  --body-column <name>  Column whose string value is clustered when --patterns
                        is set (default: whole row JSON)
  -h, --help            display help for command

💡 技术拓展:--format 接受任意 ClickHouse 输出格式,常用格式对比——

格式形态适用场景
JSONEachRow(默认)每行一个 JSON 对象(NDJSON)管道进 jq、脚本逐行处理
JSON单个 JSON 文档(含 meta/statistics)人读、看列类型
TabSeparated制表符分隔进 awk/剪贴板、与 TSV 文件互换
CSV逗号分隔进表格软件

本文默认用 JSONEachRow;要看列类型元信息时用 JSON。

实测陷阱:SQL 内联 FORMAT 子句会失败。 把 FORMAT TabSeparated 直接写进 SQL 末尾,代理报语法错并退出码 1;改用 --format 旗标则正常:

$ hdx query --connection-id 6adaae2ed640de28d174c2b2 --sql "SELECT Timestamp, ServiceName, Body FROM default.otel_logs LIMIT 1 FORMAT TabSeparated"
Query failed: Syntax error: failed at position 127
(退出码 1,无 stdout)

$ hdx query --connection-id 6adaae2ed640de28d174c2b2 --format TabSeparated --sql "SELECT Timestamp, ServiceName, SeverityText, Body FROM default.otel_logs ORDER BY Timestamp DESC LIMIT 3"
2026-10-23T01:10:35.000000000Z  recreate-check  info    collector recreate check
2026-10-23T01:08:19.000000000Z  compose-final-check     info    B2 部署闭环验证 1792717699000000000
2026-10-23T00:46:03.531476487Z  hyperdx-docs-test       error   HyperDX 2.41.0 ingest smoke test from quzuo docs

退出码契约(实测验证):成功 0(结果为空时 stdout 为空也是成功);失败 1,且两类错误在 stderr 可区分——连接不存在会附上可用连接清单,SQL 错误直接给 ClickHouse 报错:

$ hdx query --connection-id deadbeef-0000 --sql "SELECT 1"
Connection "deadbeef-0000" not found.

Available connections:
  - Local ClickHouse [6adaae2ed640de28d174c2b2]
(退出码 1)
$ hdx query --connection-id 6adaae2ed640de28d174c2b2 --sql "SELECT nonexistent_col FROM default.otel_logs LIMIT 1"
Query failed: Unknown expression identifier `nonexistent_col` in scope SELECT nonexistent_col FROM default.otel_logs LIMIT 1.
(退出码 1)

日志模式聚类(--patterns):用 Drain 算法把大量行聚成模板,按出现次数排序输出。实测栈日志量小(3 条),每条各成一 pattern:

$ hdx query --connection-id 6adaae2ed640de28d174c2b2 --patterns --body-column Body --sql "SELECT Body FROM default.otel_logs LIMIT 1000"
{"pattern":"HyperDX 2.41.0 ingest smoke test from quzuo docs","count":1,"sample":"HyperDX 2.41.0 ingest smoke test from quzuo docs"}
{"pattern":"B2 部署闭环验证 1792717699000000000","count":1,"sample":"B2 部署闭环验证 1792717699000000000"}
{"pattern":"collector recreate check","count":1,"sample":"collector recreate check"}

生产日志量大时该命令的价值是把上万行原始日志压成几十个模板,sample 字段给出每类的代表样本。

4.4 终端图表(chart)

hdx chart 在终端直接渲染图表,三种模式:仪表盘 tile(-d)、即席聚合(-s 源名)、原始 SQL(--sql)。实测栈没建过仪表盘,重点验证即席模式。

按严重级别过滤计数、按服务分组:

$ hdx chart -s Logs --where 'SeverityText:error' --since 24h --display table
count() where SeverityText:error (table)
count()
      1

$ hdx chart -s Logs --group-by ServiceName --since 24h --display table
count() by ServiceName (table)
count()  ServiceName
      1  compose-final-check
      1  hyperdx-docs-test
      1  recreate-check

默认 line 显示在终端画出 ANSI 字符图(实测节选):

$ hdx chart -s Logs --since 24h
count() (line)
         2 ┼                                                                  ╭╮
           ┤                                                                  ││
           ┤                                                                  ││
       1.5 ┤                                                                 ╭╯│
           ┤                                                                 │ │
         1 ┤                                                                ╭╯ ╰╮
           ┤                                                                │   │
       0.5 ┤                                                               ╭╯   │
           ┤                                                               │    │

--json 不画图,返回原始行与列元信息(供脚本消费):

$ hdx chart -s Logs --group-by ServiceName --since 24h --json
[
  {
    "id": null,
    "name": "count() by ServiceName",
    "displayType": "line",
    "status": "ok",
    "data": [
      {
        "count()": "1",
        "ServiceName": "hyperdx-docs-test",
        "__hdx_time_bucket": "2026-10-23T00:30:00Z"
      },
      {
        "count()": "1",
        "ServiceName": "recreate-check",
        "__hdx_time_bucket": "2026-10-23T01:00:00Z"
      },
      {
        "count()": "1",
        "ServiceName": "compose-final-check",
        "__hdx_time_bucket": "2026-10-23T01:00:00Z"
      }
    ],
    "meta": [
      {
        "name": "count()",
        "type": "UInt64"
      },

参数要点(实测确认):--display 可选 line(默认)/stacked_bar/number/table/bar/pie;时间范围用 --since 15m|1h|7d 或 --from/--to(ISO 8601、日期或 now-24h 相对值);追踪源的延迟分位用 --agg quantile --level 0.95 --value Duration;输出重定向时 ANSI 颜色自动剥离。

4.5 交互式 TUI 与 source maps 上传

hdx tui 是全功能终端界面(搜索、实时 tail、行详情、追踪瀑布、告警与仪表盘浏览)。交互界面无法以回显呈现,此处给出实测的参数表(hdx tui --help):

Usage: hdx tui [options]

Interactive TUI for event search and tail

Options:
  -a, --app-url <url>  HyperDX app URL
  -q, --query <query>  Initial Lucene search query
  --source <name>      Source name (skips picker)
  -f, --follow         Start in follow/live tail mode
  -h, --help           display help for command

常用启动形态:hdx tui --source Logs(跳过源选择)、hdx tui -q 'SeverityText:error' -f(带过滤条件进入实时 tail 模式)。界面内按 ? 显示键位帮助,q 退出。

hdx upload-sourcemaps 用于构建流水线里上传 JS source maps(堆栈反混淆),参数实测回显:

Usage: hdx upload-sourcemaps [options]

Upload JavaScript source maps to HyperDX for stack trace de-obfuscation

Options:
  -k, --serviceKey <string>   The HyperDX service account API key
  -u, --apiUrl [string]       An optional api url for self-hosted deployments
  -rid, --releaseId [string]  An optional release id to associate the
                              sourcemaps with
  -p, --path [string]         Sets the directory of where the sourcemaps are
                              (default: ".")
  -bp, --basePath [string]    An optional base path for the uploaded sourcemaps
  --apiVersion [string]       The API version to use (v1 for HyperDX V1 Cloud,
                              v2 for latest) (default: "v1")
  -h, --help                  display help for command

自托管部署调用时 -u 填 HyperDX 的对外地址,-k 填服务账号 API key;该命令面向前端构建流水线,本文栈无前端构建产物,参数以 --help 回显为准。

五、Docker Compose 栈编排命令

四服务栈的日常启停、状态与日志全部经 docker compose 操作,须在编排文件所在目录执行(本文即 B2 篇的部署目录)。本节命令逐条实测。

5.1 配置校验与状态查看

改完 docker-compose.yml 先做无副作用校验(语法与引用错误,--quiet 无输出即通过):

$ docker compose config --quiet && echo 'config OK'
config OK

状态查看的两种常用形态:

$ docker compose ps --format 'table {{.Name}}\t{{.Status}}'
NAME                                  STATUS
hyperdx-2410-stack-app-1              Up 6 hours (healthy)
hyperdx-2410-stack-ch-server-1        Up 6 hours
hyperdx-2410-stack-db-1               Up 6 hours
hyperdx-2410-stack-otel-collector-1   Up 6 hours (healthy)

$ docker compose ps --format 'table {{.Name}}\t{{.Image}}\t{{.Status}}'
NAME                                  IMAGE                                         STATUS
hyperdx-2410-stack-app-1              hyperdx/hyperdx:2.41.0                        Up 6 hours (healthy)
hyperdx-2410-stack-ch-server-1        clickhouse/clickhouse-server:26.1-alpine      Up 6 hours
hyperdx-2410-stack-db-1               mongo:5.0.32-focal                            Up 6 hours
hyperdx-2410-stack-otel-collector-1   clickhouse/clickstack-otel-collector:2.41.0   Up 6 hours (healthy)

健康判据:app 与 otel-collector 带 (healthy) 即栈可用;ch-server 与 db 的编排未配 healthcheck,Up 即正常。端口到服务的映射用 port 查(对照编排里的映射关系,排查「端口被谁占了」时用):

$ docker compose port app 8130
0.0.0.0:8130
$ docker compose port otel-collector 4318
0.0.0.0:8138

5.2 镜像与进程拓扑(images / top)

images 核对四个镜像的 tag 与体积(升级前后的对照基线):

$ docker compose images
CONTAINER                             REPOSITORY                             TAG                 PLATFORM            IMAGE ID            SIZE                CREATED
hyperdx-2410-stack-app-1              hyperdx/hyperdx                        2.41.0              linux/amd64         00c6d5b7f3d4        979MB               10 days ago
hyperdx-2410-stack-ch-server-1        clickhouse/clickhouse-server           26.1-alpine         linux/amd64         369bd6894256        988MB               5 months ago
hyperdx-2410-stack-db-1               mongo                                  5.0.32-focal        linux/amd64         3b6c281e1c08        1.08GB              9 months ago
hyperdx-2410-stack-otel-collector-1   clickhouse/clickstack-otel-collector   2.41.0              linux/amd64         04725e89ed30        497MB               10 days ago

top 看 app 内部进程树——它不是单进程服务,而是 concurrently 托管的三个进程(API、APP、ALERT-TASK),排障时先分清日志来自哪一路(实测节选):

$ docker compose top app
SERVICE  #   UID    PID      PPID     C   STIME  TTY  TIME      CMD
app      1   yahui  2580377  2580348  0   09:06  ?    00:00:00  sh /etc/local/entry.sh
app      1   yahui  2580465  2580377  0   09:06  ?    00:00:00  node ./node_modules/.bin/concurrently --kill-others-on-fail --names=API,APP,ALERT-TASK PORT=8136 HYPERDX_APP_PORT=8130 ./packages/api/bin/hyperdx api cd ./packages/app/packages/app && HOSTNAME='0.0.0.0' HYPERDX_API_PORT=8136 PORT=8130 node server.js ./packages/api/bin/hyperdx task check-alerts
app      1   yahui  2580523  2580465  0   09:06  ?    00:00:12  node -r @hyperdx/node-opentelemetry/build/src/tracing /app/packages/api/bin/../build/index.js
app      1   yahui  2580526  2580465  0   09:06  ?    00:00:01  next-server (v
app      1   yahui  2580527  2580465  0   09:06  ?    00:00:08  node -r @hyperdx/node-opentelemetry/build/src/tracing /app/packages/api/bin/../build/tasks/index.js check-alerts

5.3 生命周期与日志

常用生命周期命令(均在编排目录执行):

# 修改编排后增量生效(只重建受影响服务,数据卷不动)
docker compose up -d

# 重启单个服务(collector 重启后会自动重新向 app 领取 OpAMP 配置)
docker compose restart otel-collector

# 停止与销毁(-v 连同匿名卷;本编排数据在 bind mount,需另行清理 volumes/ 目录)
docker compose down
docker compose down -v

日志排查用 logs,--tail 控制行数、-f 跟随(第八章有日志形态详解):

$ docker compose logs --tail=3 app
app-1  | [ALERT-TASK] {"level":30,"time":1792737600038,"pid":25,"hostname":"b607c48a34ab","msg":"Lost connection to MongoDB server"}
app-1  | [ALERT-TASK] {"level":30,"time":1792737600038,"pid":25,"hostname":"b607c48a34ab","msg":"check-alerts finished in 10.09 ms"}
app-1  | [API] {"level":30,"time":1792737618182,"pid":21,"hostname":"b607c48a34ab","req":{"id":779,"method":"GET","url":"/health","query":{},"params":

六、Collector 配置校验(otelcontribcol validate)

collector 容器内有两个二进制:/opampsupervisor(PID 1,管理配置下发)与 /otelcontribcol(真正的 OTel Collector,运行参数 --config /etc/otel/supervisor-data/effective.yaml --feature-gates service.AllowNoPipelines,实测 ps 输出)。supervisor 把 app 经 OpAMP 下发的动态配置渲染成 effective.yaml,collector 实际加载的就是它——校验也应针对这个文件。

改配置(如自定义 receiver、排障怀疑配置损坏)后,用内置 validate 子命令做无副作用校验。场景一:正常配置——容器内环境变量齐全,${env:...} 占位符都能解析,校验通过无任何输出:

$ docker exec hyperdx-2410-stack-otel-collector-1 /otelcontribcol validate --config=/etc/otel/supervisor-data/effective.yaml
(退出码 0,无输出)

场景二:配置文件语法损坏——把 YAML 值写坏(数组括号不闭合)后校验,报解析错误并退出码 1:

$ docker exec hyperdx-2410-stack-otel-collector-1 sh -c 'printf "receivers:\n  otlp:\n    protocols:\n      grpc:\n        endpoint: [broken\n" > /etc/otel/supervisor-data/bad.yaml && /otelcontribcol validate --config=/etc/otel/supervisor-data/bad.yaml'
Error: failed to get config: cannot resolve the configuration: retrieved value (type=string) cannot be used as a Conf: assuming string type since contents are not valid YAML: yaml: line 4: did not find expected ',' or ']'
(退出码 1)

场景三:环境变量缺失——effective.yaml 大量引用 ${env:CLICKHOUSE_ENDPOINT} 这类占位符,清空环境变量后校验,ClickHouse 导出器因拿不到 endpoint 报错(退出码 1):

$ docker exec hyperdx-2410-stack-otel-collector-1 env -i /otelcontribcol validate --config=/etc/otel/supervisor-data/effective.yaml
Error: exporters::clickhouse/rrweb: endpoint must be specified
parse dsn address failed
exporters::clickhouse: endpoint must be specified
parse dsn address failed
(退出码 1)

三个场景合起来的结论:validate 只校验「YAML 语法 + 组件装配 + ${env:} 可解析」,不校验 ClickHouse/Mongo 是否真的可达(endpoint 写错但占位符有值时,validate 依然通过)。连通性问题要看 collector 运行日志(第八章 8.1)。

💡 技术拓展:ClickStack collector 的配置生效方式有两条路线——

路线机制适用场景
OpAMP 动态下发(默认)app 把 receiver/导出器配置经 OpAMP 推给 supervisor,渲染为 effective.yaml 并热加载标准部署;配置随 UI/环境变量变化自动同步
静态文件挂载环境变量 CUSTOM_OTELCOL_CONFIG_FILE 指向自定义配置需要预置 app 未覆盖的 collector 组件时

本栈采用默认动态下发;两条路线的参数细节见 《HyperDX 2.41.0 配置参数详解》第三章。

七、外部 API v2 调用(curl 实测)

HyperDX 提供版本化的外部 REST API(OpenAPI 标题 HyperDX External API 2.0.0),挂在 API 端口(本文 8136)的 /api/v2 前缀下,Bearer 方式认证,覆盖搜索、图表序列、告警、仪表盘、Saved Search、Webhook、团队等资源。本节全部请求用 curl 实测。

7.1 双密钥体系与认证

ClickStack 有两套独立密钥,用途与传递方式都不同:

💡 技术拓展:两套密钥对比——

密钥归属传递方式用途
团队 ingest key团队(teams 集合)OTLP 请求头 authorization: <key>(裸值,不加 Bearer)遥测摄入(4317/4318)
用户 accessKey用户(users 集合)REST 请求头 Authorization: Bearer <key>外部 API v2 的程序化调用

本文外部 API 调用一律用 accessKey;ingest key 的摄入用法见 B2 篇第 4.5 节。

两个密钥都可直接从 MongoDB 取(UI 的 Team Settings 页同样可见):

$ docker exec hyperdx-2410-stack-db-1 mongosh --quiet hyperdx --eval 'JSON.stringify(db.users.find({},{email:1,accessKey:1}).toArray())'
[{"_id":"6adaae2ed640de28d174c2a9","email":"root@quzuo.net","accessKey":"b758ccd0-1eb5-4179-a365-82fdffe3e95d"}]
$ docker exec hyperdx-2410-stack-db-1 mongosh --quiet hyperdx --eval 'JSON.stringify(db.teams.find({},{apiKey:1}).toArray())'
[{"_id":"6adaae2ed640de28d174c2ad","apiKey":"cca923a9-201f-4aa6-9d2d-98d016261d61"}]

7.2 元数据端点(team / connections / sources)

先验证认证并拿到资源 ID(后续搜索需要 sourceId,SQL 代理需要 connectionId):

$ curl -s -w '\nHTTP=%{http_code}\n' --max-time 10 http://localhost:8136/api/v2/team \
    -H "Authorization: Bearer b758ccd0-1eb5-4179-a365-82fdffe3e95d"
{"data":{"id":"6adaae2ed640de28d174c2ad","name":"root@quzuo.net's Team"}}
HTTP=200

$ curl -s --max-time 10 http://localhost:8136/api/v2/connections \
    -H "Authorization: Bearer b758ccd0-1eb5-4179-a365-82fdffe3e95d"
{"data":[{"id":"6adaae2ed640de28d174c2b2","name":"Local ClickHouse","host":"http://ch-server:8123","username":"default","createdAt":"2026-10-23T00:45:34.864Z","updatedAt":"2026-10-23T00:45:34.864Z"}]}

/api/v2/sources 返回四类源(截取关键字段):

$ curl -s --max-time 10 http://localhost:8136/api/v2/sources \
    -H "Authorization: Bearer b758ccd0-1eb5-4179-a365-82fdffe3e95d"
(实测返回四个对象,关键映射如下)
6adaae2ed640de28d174c2b6 | log    | Logs    | otel_logs
6adaae2ed640de28d174c2b8 | trace  | Traces  | otel_traces
6adaae2ed640de28d174c2ba | metric | Metrics | (无单一表,按指标类型分表)
6adaae2ed640de28d174c2bc | session | Sessions | hyperdx_sessions

7.3 搜索与图表序列(search / charts/series)

POST /api/v2/search 对应网页端 Search 面板:给源 ID、时间窗、Lucene 过滤式与 SELECT 列,返回按时间倒序的原始行。实测检索近 24 小时的 error 日志:

$ curl -s -w '\nHTTP=%{http_code}\n' --max-time 15 -X POST http://localhost:8136/api/v2/search \
    -H "Authorization: Bearer b758ccd0-1eb5-4179-a365-82fdffe3e95d" \
    -H 'Content-Type: application/json' \
    -d '{"sourceId":"6adaae2ed640de28d174c2b6","startTime":"2026-10-22T07:00:00Z","endTime":"2026-10-23T07:00:00Z","where":"SeverityText:error","select":"Timestamp,SeverityText,Body,ServiceName","maxResults":10}'
{"data":[{"Timestamp":"2026-10-23T00:46:03.531476487Z","SeverityText":"error","Body":"HyperDX 2.41.0 ingest smoke test from quzuo docs","ServiceName":"hyperdx-docs-test"}],"rows":1}
HTTP=200

POST /api/v2/charts/series 对应图表时间序列:毫秒时间戳 + 粒度 + 聚合定义数组。实测按 1 小时粒度统计日志行数:

$ curl -s -w '\nHTTP=%{http_code}\n' --max-time 15 -X POST http://localhost:8136/api/v2/charts/series \
    -H "Authorization: Bearer b758ccd0-1eb5-4179-a365-82fdffe3e95d" \
    -H 'Content-Type: application/json' \
    -d '{"startTime":1792651200000,"endTime":1792737600000,"granularity":"1h","series":[{"sourceId":"6adaae2ed640de28d174c2b6","aggFn":"count","where":"","groupBy":[]}]}'
{"data":[{"ts_bucket":1792713600000,"group":[],"series_0.data":"1"},{"ts_bucket":1792717200000,"group":[],"series_0.data":"2"}]}
HTTP=200

两个端点的参数约定(实测确认):search 的 startTime/endTime 用 ISO 8601 字符串,charts/series 用毫秒 epoch;where 用 Lucene 风格(SeverityText:error、ServiceName:api AND Duration:>1000000000);分页由 maxResults 与响应游标控制。

7.4 错误响应与端点总表

三类错误的实测回显,脚本里按状态码分支处理:

$ curl -s -w '\nHTTP=%{http_code}\n' --max-time 10 http://localhost:8136/api/v2/team -H "Authorization: Bearer 00000000-0000-0000-0000-000000000000"
Unauthorized
HTTP=401

$ curl -s -w '\nHTTP=%{http_code}\n' --max-time 10 http://localhost:8136/api/v2/team
Unauthorized
HTTP=401

$ curl -s -w '\nHTTP=%{http_code}\n' --max-time 10 -X POST http://localhost:8136/api/v2/search \
    -H "Authorization: Bearer b758ccd0-1eb5-4179-a365-82fdffe3e95d" -H 'Content-Type: application/json' \
    -d '{"startTime":"2026-10-23T00:00:00Z","endTime":"2026-10-23T23:59:59Z"}'
{"message":"Body validation failed: sourceId: Required"}
HTTP=400

外部 API v2 的完整端面(OpenAPI 规范 packages/api/openapi.json,查询日期 2026-10-23):

资源方法与路径
团队GET /api/v2/team、GET /api/v2/team/members、POST /api/v2/team/invitation 等
连接`GET
数据源`GET
搜索与图表POST /api/v2/search、POST /api/v2/charts/series
告警`GET
仪表盘`GET
Saved Search`GET
Webhook`GET

实测结论:UI 内部端点不对外。 试图用会话 Cookie 调内部搜索路径(GET|POST /api/search)返回 Cannot GET/POST /api/search(404),带 Cookie 调 /api/v2/search 也因无 Bearer 得 401——程序化集成一律走「API 端口 + /api/v2 + Bearer accessKey」,不要试图复用网页会话。

八、日志排查与底层体检

三层日志各自负责一段:app 日志看应用行为,collector 日志看摄入与配置下发,ClickHouse/Mongo 直查看数据落没落。本节给出实测的排查入口与关键日志形态。

8.1 app 三路日志与 collector OpAMP 心跳

app 容器一路容器日志里混着三路进程日志,前缀 [API] / [APP] / [ALERT-TASK]。首启阶段的 API 关键行实测(判断配置问题先看这几条):

$ docker compose logs app 2>/dev/null | grep -E '^\S+\s+\| \[(API|ALERT-TASK)\]' | head -7
app-1  | [API] {"level":40,"time":1792716321456,"pid":21,"hostname":"33482c20926e","msg":"Token encryption is disabled: third-party tokens will be stored in plain text (set TOKEN_ENCRYPTION_KEY to enable)"}
app-1  | [API] {"level":30,"time":1792716321457,"pid":21,"hostname":"33482c20926e","msg":"API Server listening on port 8136, NODE_ENV=production"}
app-1  | [API] {"level":30,"time":1792716321457,"pid":21,"hostname":"33482c20926e","msg":"OpAMP Server listening on port 4320, NODE_ENV=production"}
app-1  | [API] {"level":50,"time":1792716321457,"pid":21,"hostname":"33482c20926e","msg":"EXPRESS_SESSION_SECRET is missing or uses the public legacy value: generated a random secret for this process. Users are signed out whenever it restarts, and replicas do not share sessions. Configure a random secret shared by all replicas to keep sessions."}
app-1  | [API] {"level":30,"time":1792716321477,"pid":21,"hostname":"33482c20926e","msg":"Connection established to MongoDB"}
app-1  | [ALERT-TASK] {"level":30,"time":1792716360054,"pid":25,"hostname":"33482c20926e","msg":"Connection established to MongoDB"}
app-1  | [ALERT-TASK] {"level":30,"time":1792716480034,"pid":25,"hostname":"33482c20926e","msg":"Connection established to MongoDB"}

逐条解读:API Server listening(API 端口就绪)、OpAMP Server listening(4320 就绪,collector 才能领到配置)、EXPRESS_SESSION_SECRET 为 level 50(error 级)的告警——未设该变量则每次重启全员登出(生产要设,见 C1 篇 5.5 节);ALERT-TASK 的 Connection established to MongoDB 约每 120 秒重复一次,是告警检查任务的周期心跳,属正常而非故障。

collector 侧的排查入口是 supervisor 心跳与 agent 日志。Connected to the OpAMP server. 约每 30 秒重复一次(心跳,正常),agent 自身日志在容器内 /etc/otel/supervisor-data/agent.log:

$ docker compose logs --tail=400 otel-collector 2>/dev/null | grep 'Connected to the OpAMP' | head -3
otel-collector-1  | {"level":"info","ts":1792725646.8463557,"logger":"supervisor","caller":"supervisor/supervisor.go:1051","msg":"Connected to the OpAMP server."}
otel-collector-1  | {"level":"info","ts":1792725676.85377,"logger":"supervisor","caller":"supervisor/supervisor.go:1051","msg":"Connected to the OpAMP server."}
otel-collector-1  | {"level":"info","ts":1792725706.8613946,"logger":"supervisor","caller":"supervisor/supervisor.go:1051","msg":"Connected to the OpAMP server."}

OTLP 端口不监听的排查链(B2 篇常见问题 3 的命令级展开):① docker compose top otel-collector 确认 /otelcontribcol 进程在跑且带 service.AllowNoPipelines gate;② grep 上表心跳确认 OpAMP 通路;③ 读 agent.log 找 receiver 绑定报错;④ 仍无端口则重启 collector 强制重领配置(docker compose restart otel-collector)。

8.2 ClickHouse 与 Mongo 快速体检

绕开应用层直查存储,确认「数据到底落没落库」。经 docker compose exec 执行(与 B2 篇的 docker exec 形态等价,前缀换成 compose 可少写容器名):

$ docker compose exec ch-server clickhouse-client --query "SELECT version()"
26.1.12.23
$ docker compose exec ch-server clickhouse-client --query "SELECT ServiceName, count() AS c FROM default.otel_logs GROUP BY ServiceName ORDER BY c DESC"
hyperdx-docs-test     1
recreate-check        1
compose-final-check   1

$ docker compose exec db mongosh --quiet hyperdx --eval 'JSON.stringify(db.teams.find({},{apiKey:1}).toArray())'
[{"_id":"6adaae2ed640de28d174c2ad","apiKey":"cca923a9-201f-4aa6-9d2d-98d016261d61"}]

表清单体检(新 schema 的 14 张表/视图,全量实测):

$ docker compose exec ch-server clickhouse-client --query "SELECT name, engine FROM system.tables WHERE database='default' ORDER BY name FORMAT TSV"
hyperdx_sessions        MergeTree
otel_logs       MergeTree
otel_logs_attr_kv_rollup_15m_mv MaterializedView
otel_logs_kv_rollup_15m SummingMergeTree
otel_metrics_exponential_histogram      MergeTree
otel_metrics_gauge      MergeTree
otel_metrics_histogram  MergeTree
otel_metrics_sum        MergeTree
otel_metrics_summary    MergeTree
otel_traces     MergeTree
otel_traces_kv_rollup_15m       SummingMergeTree
otel_traces_kv_rollup_15m_mv    MaterializedView
otel_traces_trace_id_ts MergeTree
otel_traces_trace_id_ts_mv      MaterializedView

三方交叉验证的用法:一条日志「摄入了但搜不到」时,先 curl 打 OTLP HTTP 看 200(B2 篇 4.5 节),再 clickhouse-client 直查 otel_logs(本节),最后 hdx query 走代理查(4.3 节)——三处结果差异能把故障定位到摄入、落库或查询代理三者之一。

九、注意事项

  1. 端口分工:hdx 的 --app-url 与 hdx auth login -a 指 UI 端口(8130);curl 的 /api/v2、/health、/register、/login 指 API 端口(8136)。混用是新手第一坑(见第十章问题 1)。
  2. 退出码契约:hdx query 成功 0 / 失败 1,脚本里判断 $? 即可;docker compose config --quiet 以「无输出」为通过,不能只看退出码之外的提示。
  3. validate 的边界:otelcontribcol validate 过了不代表连通性 OK——它不探测 ClickHouse/Mongo 可达性(第六章场景三实测)。
  4. 时间参数类型不一致:search 用 ISO 8601 字符串、charts/series 用毫秒 epoch(7.3 实测),脚本封装时别统一成一种。
  5. 密钥最小化:accessKey 等同账号凭据,脚本是放进环境变量或凭据管理,不要落进版本库;ingest key 泄露的后果是任何人都能往栈里写遥测。
  6. 日志前缀即进程:app 容器日志先按 [API]/[APP]/[ALERT-TASK] 前缀分路再看内容,能省一半排查时间(8.1 实测)。

十、常见问题

1. hdx auth login 报连接失败或超时,但网页能打开。 -a 传成了 API 端口(8136)。CLI 的 --app-url 依赖 Next.js 侧的代理路径,必须指 UI 端口(8130)。curl 直调 API 时才用 8136。两个端口各自服务,见 B2 篇常见问题 5 的实测。

2. hdx query 报 Syntax error: failed at position N。 SQL 末尾带了内联 FORMAT <format> 子句——HyperDX 代理不接受(4.3 实测)。把格式改到 --format 旗标上。

3. curl 调 /api/v2 下任意端点全部 401。 检查三点:用的是 accessKey(不是 ingest key)、头格式为 Authorization: Bearer <key>(ingest key 的 OTLP 头才是裸值)、请求打在 API 端口。三者任一错都得到 Unauthorized(7.4 实测回显)。

4. POST /api/v2/search 返回 400 Body validation failed: sourceId: Required。 请求体缺 sourceId。先 GET /api/v2/sources 拿源 ID(7.2 实测),或 hdx sources --json 获取同一份映射。

5. docker compose port 报 error: no such service 或空输出。 port 的第二个参数是容器内端口而非宿主端口,且服务必须带对应映射。查 4318 映射要写 docker compose port otel-collector 4318(5.1 实测得 0.0.0.0:8138)。

6. validate 场景三报 endpoint must be specified,但我的配置明明有 endpoint。 那是 env -i 清空环境变量后的预期现象——配置里写的是 ${env:CLICKHOUSE_ENDPOINT} 占位符(第六章场景三实测)。真实运行时占位符由容器环境变量解析,勿据此误判配置损坏。

十一、参考链接