groundcover-cli
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGroundcover CLI
Groundcover CLI
Invoke the binary (install via ). Source of truth is .
groundcoverbrew install paymog/tap/groundcoverpaymog/groundcover-cliAuth (required before any command)
认证(所有命令执行前必须完成)
sh
export GROUNDCOVER_API_KEY=gcsa_... # must be a service-account keyThe key must be a service-account key: prefix , exactly 40 chars. Anything else is rejected with a clear message — or . Don't confuse key types:
gcsa_Invalid token prefix. Expected 'gcsa_'Invalid token length (N, expected 40)- — service-account key. This is the one query/CRUD commands need.
gcsa_… - — ingestion key (data push). Will NOT authenticate the API; wrong type.
gcik_…
A 401 with a correctly-formatted key means the key belongs to a different tenant than the one you're querying.
gcsa_If you don't have a key handy, mint one with using an already-valid key, then for the session.
gcsa_groundcover service-accounts createexport GROUNDCOVER_API_KEY=gcsa_…Defaults baked in:
--base-url https://api.groundcover.com--backend-id groundcover
No tenant UUID default — set / to send an header on non-grafana passthrough calls (e.g. cross-tenant access). The embedded Grafana endpoints ignore it (see the Grafana section below).
--tenant-uuidGROUNDCOVER_TENANT_UUIDX-Tenant-UUIDraw …raw grafana …The commands do NOT use the key at all — they need a Grafana service account token (). Set / .
raw grafana …gcsa_glsa_…GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN--grafana-tokenOverride env: , , , (or equivalents). Same names work as , , , , flags.
GROUNDCOVER_BACKEND_IDGROUNDCOVER_TENANT_UUIDGROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKENGROUNDCOVER_BASE_URLGC_*--api-key--backend-id--tenant-uuid--grafana-token--base-urlsh
export GROUNDCOVER_API_KEY=gcsa_... # 必须是服务账号密钥密钥必须为服务账号密钥:前缀为,长度恰好40个字符。 不符合要求的密钥会被拒绝,并返回明确提示信息——或。请勿混淆密钥类型:
gcsa_Invalid token prefix. Expected 'gcsa_'Invalid token length (N, expected 40)- — 服务账号密钥。查询/CRUD命令需使用此类密钥。
gcsa_… - — 数据摄入密钥(用于推送数据)。无法用于API认证,属于错误类型。
gcik_…
若格式正确的密钥返回401错误,说明该密钥所属的租户与当前查询的租户不一致。
gcsa_若手头没有密钥,可使用已验证的密钥执行生成新密钥,然后在会话中执行。
gcsa_groundcover service-accounts createexport GROUNDCOVER_API_KEY=gcsa_…内置默认配置:
--base-url https://api.groundcover.com--backend-id groundcover
无默认租户UUID——在非Grafana的透传调用中(如跨租户访问),需设置 / 以发送请求头。嵌入式Grafana的端点会忽略该配置(见下文Grafana章节)。
raw …--tenant-uuidGROUNDCOVER_TENANT_UUIDX-Tenant-UUIDraw grafana …raw grafana …gcsa_glsa_…GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN--grafana-token可通过环境变量覆盖默认配置:、、、(或等价的前缀变量)。这些变量名称与、、、、标志一致。
GROUNDCOVER_BACKEND_IDGROUNDCOVER_TENANT_UUIDGROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKENGROUNDCOVER_BASE_URLGC_*--api-key--backend-id--tenant-uuid--grafana-token--base-urlStored profiles (alternative to env vars)
存储配置文件(环境变量的替代方案)
Instead of exporting every session, credentials can be saved in named profiles. The API key goes in the OS keyring; metadata (backend ID, base URL, tenant UUID) lives in .
GROUNDCOVER_API_KEY~/.config/groundcover/profiles.yamlsh
groundcover auth login [name] --backend-id <id> # prompts for key (or --key); validates before saving
groundcover auth list # * marks default
groundcover auth default <name> # change default
groundcover --profile <name> <command> # use a profile for one command
groundcover auth status # show resolved source
groundcover auth token # print resolved key
groundcover auth logout <name> [-f]Precedence: / env → → default profile. An explicit key plus is rejected as ambiguous. sets the profile via env.
--api-keyGROUNDCOVER_API_KEY--profile <name>--profileGROUNDCOVER_PROFILEOther global flags: (default 30s), (don't reformat JSON response).
--timeout--raw无需每次会话都导出,可将凭证保存到命名配置文件中。API密钥存储在操作系统密钥环中;元数据(后端ID、基础URL、租户UUID)存储在中。
GROUNDCOVER_API_KEY~/.config/groundcover/profiles.yamlsh
groundcover auth login [name] --backend-id <id> # 提示输入密钥(或使用--key参数);保存前会验证有效性
groundcover auth list # *标记默认配置文件
groundcover auth default <name> # 修改默认配置文件
groundcover --profile <name> <command> # 为单次命令指定配置文件
groundcover auth status # 显示已解析的凭证来源
groundcover auth token # 打印已解析的密钥
groundcover auth logout <name> [-f]优先级:/环境变量 → → 默认配置文件。同时指定显式密钥和会因歧义被拒绝。可通过环境变量设置配置文件。
--api-keyGROUNDCOVER_API_KEY--profile <name>--profileGROUNDCOVER_PROFILE其他全局标志:(默认30秒)、(不格式化JSON响应)。
--timeout--rawTwo surfaces
两种调用方式
| Surface | When to use |
|---|---|
SDK-backed ( | First choice. Stable contracts, typed request bodies. |
Raw HAR-derived ( | Endpoint missing from SDK, or you need a runner feature SDK lacks (see below). |
Always try the SDK form first.
| 方式 | 使用场景 |
|---|---|
基于SDK ( | 首选方式。契约稳定,请求体类型化。 |
基于原始HAR ( | SDK未包含目标端点,或需要SDK不具备的运行器功能(见下文)。 |
优先尝试基于SDK的调用方式。
What raw gives you that SDK does not
原始方式具备的SDK未提供功能
- — deep-merge overrides on top of the body
--set dotted.path=value - (repeatable) — arbitrary querystring overrides
--query key=value - Built-in default body captured from the webapp HAR (SDK requires explicit /
--body-file)--body-json - Sends on non-grafana raw calls when you set
X-Tenant-UUID/--tenant-uuid(for cross-tenant access; the SDK transport otherwise derives tenant from the API key). GrafanaGROUNDCOVER_TENANT_UUIDcommands don't use it — they authenticate with theraw grafana …token instead.glsa_ - to discover every captured endpoint
groundcover raw list
Both surfaces support (json/yaml), , and output.
--body-file--body-json--raw- — 在请求体基础上进行深度合并覆盖
--set dotted.path=value - (可重复) — 任意查询字符串覆盖
--query key=value - 内置从Web应用HAR捕获的默认请求体(SDK需要显式指定/
--body-file)--body-json - 当设置/
--tenant-uuid时,非Grafana的原始调用会发送GROUNDCOVER_TENANT_UUID请求头(用于跨租户访问;SDK传输层会从API密钥中推导租户信息)。Grafana的X-Tenant-UUID命令不使用该配置——它们通过raw grafana …令牌进行认证。glsa_ - 可发现所有已捕获的端点
groundcover raw list
两种方式均支持(json/yaml格式)、和输出。
--body-file--body-json--rawEndpoints that are raw-only (no SDK command yet)
仅支持原始方式的端点(暂无SDK命令)
Reach for for any of these; the SDK has the parent resource but not the drilldown:
groundcover raw …- logs: ,
filters(SDK only hasvelocity)search - traces: ,
attributes,details,errors,filters,insights,latencies,requests(SDK only hasvalues-distribution)search - metrics: ,
cardinality,cardinality-graph,discovery,labels-cardinality,query-range(SDK hasresources errors|latencies|list|requests,query,names,keys)values - prometheus: (raw Prom passthrough — handy for ad-hoc PromQL via
prometheus api query)--query query='up' - Grafana dashboards: ,
grafana search,grafana dashboards get|save|delete,grafana dashboards permissions get|update,grafana dashboards versions|get|restore,grafana folders list|get|create|update|delete,grafana folders permissions get|update,grafana annotations list|create|update|delete,grafana prometheus rules,grafana datasources label-valuesgrafana ds query - monitors drilldowns: ,
instances filters|query|timeline,labels keys,silences,summary filters|query(SDK only has CRUD)timeline - k8s drilldowns: ,
configmaps|cronjob|daemonsets|deployments|jobs|pods|pvcs|replicasets|statefulsets list,container info,context events,namespaces info|list,nodes info-with-resources|list|resources|usage top10,pod container usage,pods status-over-time,workloads availability|events|usage top10,network connections|cross-az|cross-az-regions|partners|throughput|top-connections(SDK only hasevents search-time-series,clusters,workloads,events-search)events-over-time - infra:
infra hosts info-with-resources - resources / RUM: ,
resources apis errors|filters|latencies|list|requests,rum sessions filters|querysources list - pipelines stats: ,
pipelines logs current-stats(SDK only does config CRUD)pipelines traces current-stats - tenant / billing / RBAC reads: ,
rbac seatsUsage,rbac tenant ai-settings,rbac tenant settings,backend settings,billing methodagent token-budgets|token-usage|token-usage history|token-usage tenant - storage management: for retention, gcQL exception rules, and index-tier settings
storage-management get|update --data-type <logs|traces|events|measurements|monitor_instance> - misc: ,
graph,graph filters,views member,views member defaults,migrations,connectors list org|personal,synthetics rules,aggregations metrics config|config defaultintegrations data config
Run / to confirm the exact name before invoking.
groundcover raw listgroundcover raw list <group>以下场景需使用;SDK仅包含父级资源,不支持细分操作:
groundcover raw …- 日志: 、
filters(SDK仅支持velocity)search - 链路追踪: 、
attributes、details、errors、filters、insights、latencies、requests(SDK仅支持values-distribution)search - 指标: 、
cardinality、cardinality-graph、discovery、labels-cardinality、query-range(SDK支持resources errors|latencies|list|requests、query、names、keys)values - Prometheus: (原始Prom透传——便于通过
prometheus api query执行临时PromQL查询)--query query='up' - Grafana仪表板: 、
grafana search、grafana dashboards get|save|delete、grafana dashboards permissions get|update、grafana dashboards versions|get|restore、grafana folders list|get|create|update|delete、grafana folders permissions get|update、grafana annotations list|create|update|delete、grafana prometheus rules、grafana datasources label-valuesgrafana ds query - 监控器细分操作: 、
instances filters|query|timeline、labels keys、silences、summary filters|query(SDK仅支持CRUD)timeline - K8s细分操作: 、
configmaps|cronjob|daemonsets|deployments|jobs|pods|pvcs|replicasets|statefulsets list、container info、context events、namespaces info|list、nodes info-with-resources|list|resources|usage top10、pod container usage、pods status-over-time、workloads availability|events|usage top10、network connections|cross-az|cross-az-regions|partners|throughput|top-connections(SDK仅支持events search-time-series、clusters、workloads、events-search)events-over-time - 基础设施:
infra hosts info-with-resources - 资源/RUM: 、
resources apis errors|filters|latencies|list|requests、rum sessions filters|querysources list - 流水线统计: 、
pipelines logs current-stats(SDK仅支持配置CRUD)pipelines traces current-stats - 租户/计费/RBAC读取: 、
rbac seatsUsage、rbac tenant ai-settings、rbac tenant settings、backend settings、billing methodagent token-budgets|token-usage|token-usage history|token-usage tenant - 存储管理: 用于配置保留策略、gcQL例外规则和索引层级设置
storage-management get|update --data-type <logs|traces|events|measurements|monitor_instance> - 其他: 、
graph、graph filters、views member、views member defaults、migrations、connectors list org|personal、synthetics rules、aggregations metrics config|config defaultintegrations data config
调用前可执行 / 确认准确名称。
groundcover raw listgroundcover raw list <group>SDK-backed CRUD pattern
基于SDK的CRUD模式
sh
groundcover <resource> list [--query '<filter>']
groundcover <resource> get <id>
groundcover <resource> create --body-file <file> # or --body-json '<json>'
groundcover <resource> update <id> --body-file <file>
groundcover <resource> delete <id>Body files: or .
.json.yamlsh
groundcover <resource> list [--query '<filter>']
groundcover <resource> get <id>
groundcover <resource> create --body-file <file> # 或--body-json '<json>'
groundcover <resource> update <id> --body-file <file>
groundcover <resource> delete <id>请求体文件支持或格式。
.json.yamlResources (all follow the pattern above)
资源(均遵循上述模式)
- dashboards — also ,
archive <id>restore <id> - monitors —
--query 'monitor_name = "cpu"' - silences —
list --active - recurring-silences
- connected-apps —
--query 'type:slack-webhook' - notification-routes —
--query 'prod' - synthetics
- secrets — also
hash <id> - workflows
- dashboards — 额外支持、
archive <id>restore <id> - monitors — 支持
--query 'monitor_name = "cpu"' - silences — 支持
list --active - recurring-silences
- connected-apps — 支持
--query 'type:slack-webhook' - notification-routes — 支持
--query 'prod' - synthetics
- secrets — 额外支持
hash <id> - workflows
Auth / RBAC
认证/RBAC
sh
groundcover api-keys list
groundcover api-keys create --body-file key.json
groundcover service-accounts list
groundcover service-accounts create --body-file sa.json
groundcover ingestion-keys list
groundcover policies list
groundcover policies apply --body-file policy.json
groundcover policies audit-trail <id>sh
groundcover api-keys list
groundcover api-keys create --body-file key.json
groundcover service-accounts list
groundcover service-accounts create --body-file sa.json
groundcover ingestion-keys list
groundcover policies list
groundcover policies apply --body-file policy.json
groundcover policies audit-trail <id>Pipeline singletons (get
/ create
/ update
/ delete
, no id)
getcreateupdatedelete流水线单例(仅支持get
/ create
/ update
/ delete
,无需ID)
getcreateupdatedeletesh
groundcover logs-pipeline get
groundcover metrics-pipeline get
groundcover traces-pipeline get
groundcover metrics-aggregator getsh
groundcover logs-pipeline get
groundcover metrics-pipeline get
groundcover traces-pipeline get
groundcover metrics-aggregator getIntegrations (typed)
集成(类型化)
sh
groundcover integrations list
groundcover integrations describe <type>
groundcover integrations create <type> --body-file config.json
groundcover integrations update <type> <id> --body-file config.jsonsh
groundcover integrations list
groundcover integrations describe <type>
groundcover integrations create <type> --body-file config.json
groundcover integrations update <type> <id> --body-file config.jsonObservability / read commands (body-file driven)
可观测性/读取命令(基于请求体文件)
sh
groundcover logs search --body-file body.json
groundcover traces search --body-file body.json
groundcover metrics query --body-file body.json # PromQL range/instant
groundcover metrics names --body-file body.json # list metric names
groundcover metrics keys --body-file body.json # label keys for a metric
groundcover metrics values --body-file body.json # label values for a key
groundcover search discovery --body-file body.json # facet / discovery
groundcover search keys --body-file body.json
groundcover search values --body-file body.json
groundcover k8s clusters --body-file body.json
groundcover k8s workloads --body-file body.json
groundcover k8s events-search --body-file body.json
groundcover k8s events-over-time --body-file body.jsonPipe to to extract fields, or pass to dump the raw response.
jq--rawsh
groundcover logs search --body-file body.json
groundcover traces search --body-file body.json
groundcover metrics query --body-file body.json # PromQL范围/即时查询
groundcover metrics names --body-file body.json # 列出指标名称
groundcover metrics keys --body-file body.json # 指标的标签键
groundcover metrics values --body-file body.json # 标签键对应的标签值
groundcover search discovery --body-file body.json # 分面/发现
groundcover search keys --body-file body.json
groundcover search values --body-file body.json
groundcover k8s clusters --body-file body.json
groundcover k8s workloads --body-file body.json
groundcover k8s events-search --body-file body.json
groundcover k8s events-over-time --body-file body.json可通过管道传递给提取字段,或使用参数输出原始响应。
jq--rawBody templates for observability
可观测性请求体模板
Times are RFC 3339 UTC (). Wall-clock-now isn't built-in — compute with . All searches share the same shape (, , ):
2026-05-28T00:00:00Zdate -ulogs searchtraces searchk8s events-searchjson
{
"start": "2026-05-28T00:00:00Z",
"end": "2026-05-28T01:00:00Z",
"query": "<GCQL>",
"filters": "",
"sources": []
}metrics queryjson
{
"Promql": "rate(http_requests_total[5m])",
"Start": "2026-05-28T00:00:00Z",
"End": "2026-05-28T01:00:00Z",
"Step": "60",
"QueryType": "range",
"Conditions": []
}Use and omit for a point query.
"QueryType": "instant"Stepk8s workloadsk8s clustersjson
{
"conditions": [],
"sources": [],
"gcqlFilter": "namespace = 'prod'",
"limit": 100
}时间格式为RFC 3339 UTC()。未内置当前时间功能——需使用计算。所有搜索请求格式一致(、、):
2026-05-28T00:00:00Zdate -ulogs searchtraces searchk8s events-searchjson
{
"start": "2026-05-28T00:00:00Z",
"end": "2026-05-28T01:00:00Z",
"query": "<GCQL>",
"filters": "",
"sources": []
}metrics queryjson
{
"Promql": "rate(http_requests_total[5m])",
"Start": "2026-05-28T00:00:00Z",
"End": "2026-05-28T01:00:00Z",
"Step": "60",
"QueryType": "range",
"Conditions": []
}使用并省略可执行点查询。
"QueryType": "instant"Stepk8s workloadsk8s clustersjson
{
"conditions": [],
"sources": [],
"gcqlFilter": "namespace = 'prod'",
"limit": 100
}Raw commands
原始命令
sh
groundcover raw list # list groups
groundcover raw list <group> # list commands in group
groundcover raw k8s clusters list
groundcover raw dashboards get --dashboard-id <id>
groundcover raw metrics query-range --body-file body.json
groundcover raw prometheus api query --query query='up'
groundcover raw grafana dashboards get --dashboard-uid <uid>
groundcover raw grafana dashboards save --body-file dashboard.json
groundcover raw grafana folders list
groundcover raw grafana ds query --body-file query.jsonsh
groundcover raw list # 列出分组
groundcover raw list <group> # 列出分组内的命令
groundcover raw k8s clusters list
groundcover raw dashboards get --dashboard-id <id>
groundcover raw metrics query-range --body-file body.json
groundcover raw prometheus api query --query query='up'
groundcover raw grafana dashboards get --dashboard-uid <uid>
groundcover raw grafana dashboards save --body-file dashboard.json
groundcover raw grafana folders list
groundcover raw grafana ds query --body-file query.jsonStorage management
存储管理
Admins can read and update lifecycle settings for , , , (APM), and (monitor issues):
logstraceseventsmeasurementsmonitor_instancesh
groundcover raw storage-management get --data-type logs --raw \
| jq '{retention,version,cold_move_duration,cold_volume,custom_rules:(.custom_rules // [])}' \
> storage.json管理员可读取和更新、、、(APM)和(监控问题)的生命周期设置:
logstraceseventsmeasurementsmonitor_instancesh
groundcover raw storage-management get --data-type logs --raw \
| jq '{retention,version,cold_move_duration,cold_volume,custom_rules:(.custom_rules // [])}' \
> storage.jsonEdit storage.json, preserving every writable field and the complete rule list.
编辑storage.json,保留所有可写字段和完整规则列表。
groundcover raw storage-management update --data-type logs --body-file storage.json
Updates use optimistic concurrency and replace the writable settings document. Always start from `get`, carry forward its current `version`, and preserve `retention`, `cold_move_duration`, `cold_volume`, and the complete `custom_rules` list; omitting `custom_rules` removes existing rules. A successful update increments `version`. Each observed custom rule contains `name`, `retention`, and a gcQL `filters` expression.groundcover raw storage-management update --data-type logs --body-file storage.json
更新使用乐观并发机制,会替换可写设置文档。请始终从`get`命令获取当前设置,保留其`version`值,并维护`retention`、`cold_move_duration`、`cold_volume`和完整的`custom_rules`列表;省略`custom_rules`会删除现有规则。更新成功后`version`值会递增。每条自定义规则包含`name`、`retention`和gcQL`filters`表达式。Grafana native dashboards
Grafana原生仪表板
Groundcover also embeds Grafana at . These are not the same as Groundcover's first-class SDK resource, so use when you need native Grafana JSON dashboards, folders, permissions, annotations, datasource-backed variable values, or panel query execution.
/grafanadashboardsraw grafana …Auth: the embedded Grafana lives behind a session-gated proxy that ignores the API key, backend ID, and tenant UUID. A bearer/ request to just returns the ~980KB Grafana SPA (HTTP 200, ), never JSON. These commands require a Grafana service account token () instead: set (or ). Run any command without it and the CLI prints a full setup guide.
gcsa_gcsa_/grafana/api/*index.htmltext/htmlglsa_…GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN--grafana-tokenraw grafana …Generating a token (needs a groundcover tenant admin). The token comes from groundcover's official CLI (), which unfortunately also installs a binary named . Its installer drops it at and prepends that dir to your PATH, so afterwards resolves to the official CLI and shadows this one. Invoke the official binary by full path to avoid the collision:
github.com/groundcover-com/cligroundcover~/.groundcover/bin/groundcovergroundcoversh
sh -c "$(curl -fsSL https://groundcover.com/install.sh)" # installs to ~/.groundcover/bin
~/.groundcover/bin/groundcover auth login # auth flow
~/.groundcover/bin/groundcover auth generate-service-account-token # prints glsa_… once
export GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN=glsa_... # hand it to this CLITo keep this CLI as , remove the PATH line the installer adds to your shell rc.
groundcover~/.groundcover/binCommon commands:
sh
groundcover raw grafana search --query query='service slo' --query folderUIDs=general
groundcover raw grafana dashboards get --dashboard-uid streamling-pipeline-slo
groundcover raw grafana dashboards save --body-file dashboard.json
groundcover raw grafana folders list
groundcover raw grafana datasources label-values --datasource-uid <uid> --label project_id --query start=<unix> --query end=<unix>
groundcover raw grafana ds query --query ds_type=prometheus --body-file query.jsonGrafana raw commands default to (the webapp host), while normal API/SDK commands default to . Pass only for non-standard deployments.
https://app.groundcover.comhttps://api.groundcover.com--base-urlRaw flags: , , , (repeatable), generated path flags (e.g. ), . Many raw commands ship a default body captured from the webapp HAR, so you can run them with no at all and override specific fields with .
--body-json--body-file--set dotted.path=value--query key=value--dashboard-id--raw--body-*--setGroundcover在路径嵌入了Grafana。这些仪表板不同于Groundcover的一等资源(SDK资源),因此当需要原生Grafana JSON仪表板、文件夹、权限、注释、数据源支持的变量值或面板查询执行时,请使用命令。
/grafanadashboardsraw grafana …认证: 嵌入式Grafana位于会话网关代理之后,会忽略 API密钥、后端ID和租户UUID。使用Bearer/请求访问仅会返回约980KB的Grafana SPA (HTTP 200,),不会返回JSON。这些命令需要Grafana服务账号令牌():请设置(或)。未设置令牌时执行命令,CLI会打印完整的设置指南。
gcsa_gcsa_/grafana/api/*index.htmltext/htmlglsa_…GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN--grafana-tokenraw grafana …生成令牌(需要Groundcover租户管理员权限)。令牌来自Groundcover官方CLI(),该CLI安装的二进制文件也名为。其安装程序会将文件放置在并将该目录添加到PATH中,之后会指向官方CLI并覆盖当前CLI。请通过完整路径调用官方二进制文件以避免冲突:
github.com/groundcover-com/cligroundcover~/.groundcover/bin/groundcovergroundcoversh
sh -c "$(curl -fsSL https://groundcover.com/install.sh)" # 安装到~/.groundcover/bin
~/.groundcover/bin/groundcover auth login # 认证流程
~/.groundcover/bin/groundcover auth generate-service-account-token # 打印一次glsa_…令牌
export GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN=glsa_... # 将令牌提供给当前CLI若要将当前CLI保留为,请删除安装程序添加到shell配置文件中的 PATH配置。
groundcover~/.groundcover/bin常用命令:
sh
groundcover raw grafana search --query query='service slo' --query folderUIDs=general
groundcover raw grafana dashboards get --dashboard-uid streamling-pipeline-slo
groundcover raw grafana dashboards save --body-file dashboard.json
groundcover raw grafana folders list
groundcover raw grafana datasources label-values --datasource-uid <uid> --label project_id --query start=<unix> --query end=<unix>
groundcover raw grafana ds query --query ds_type=prometheus --body-file query.jsonGrafana原始命令默认使用(Web应用主机),而普通API/SDK命令默认使用。仅在非标准部署场景下需要传递参数。
https://app.groundcover.comhttps://api.groundcover.com--base-url原始命令标志:、、、(可重复)、生成的路径标志(如)、。许多原始命令内置从Web应用HAR捕获的默认请求体,因此无需指定参数即可运行,并可通过覆盖特定字段。
--body-json--body-file--set dotted.path=value--query key=value--dashboard-id--raw--body-*--setRecipes
实用示例
Last hour of error logs for a service
某服务最近一小时的错误日志
sh
END=$(date -u +%FT%TZ); START=$(date -u -v-1H +%FT%TZ) # GNU: date -u -d '1 hour ago' +%FT%TZ
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "service_name = \"api\" AND level = \"error\"",
filters: "", sources: []
}' | groundcover logs search --body-file /dev/stdin | jq '.[] | {ts:.timestamp, pod:.pod_name, body:(.body|fromjson?)}'returns a bare JSON array at the root —logs search, notjq '.[]'. The human-readable message lives in the.logs[]field as a JSON string (parse withbody). Useful top-level fields:fromjson,pod_name,container_name,namespace,workload(normalized lowercase —level/error/warning, not thedebug/WARNyou see insideDEBG),body,cluster,timestamp.trace_id
sh
END=$(date -u +%FT%TZ); START=$(date -u -v-1H +%FT%TZ) # GNU系统:date -u -d '1 hour ago' +%FT%TZ
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "service_name = \"api\" AND level = \"error\"",
filters: "", sources: []
}' | groundcover logs search --body-file /dev/stdin | jq '.[] | {ts:.timestamp, pod:.pod_name, body:(.body|fromjson?)}'返回的是顶层裸JSON数组 — 使用logs search而非jq '.[]'。可读消息位于**.logs[]**字段中,为JSON字符串(需使用body解析)。常用顶层字段:fromjson、pod_name、container_name、namespace、workload(标准化小写——level/error/warning,而非debug字段中的body/WARN)、DEBG、cluster、timestamp。trace_id
p99 latency for a workload (last 30m, PromQL)
某工作负载的p99延迟(最近30分钟,PromQL)
sh
END=$(date -u +%FT%TZ); START=$(date -u -v-30M +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
Promql: "histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{service=\"api\"}[5m])))",
Start: $s, End: $e, Step: "60", QueryType: "range", Conditions: []
}' | groundcover metrics query --body-file /dev/stdinsh
END=$(date -u +%FT%TZ); START=$(date -u -v-30M +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
Promql: "histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{service=\"api\"}[5m])))",
Start: $s, End: $e, Step: "60", QueryType: "range", Conditions: []
}' | groundcover metrics query --body-file /dev/stdinCrashlooping or restarting pods
处于崩溃循环或重启状态的Pod
sh
jq -n '{ conditions: [], sources: [], gcqlFilter: "status != \"Running\"", limit: 200 }' \
| groundcover k8s workloads --body-file /dev/stdin \
| jq '.workloads[] | select(.restarts > 0) | {name, namespace, status, restarts}'sh
jq -n '{ conditions: [], sources: [], gcqlFilter: "status != \"Running\"", limit: 200 }' \
| groundcover k8s workloads --body-file /dev/stdin \
| jq '.workloads[] | select(.restarts > 0) | {name, namespace, status, restarts}'Recent k8s events for a namespace
某命名空间的近期K8s事件
sh
END=$(date -u +%FT%TZ); START=$(date -u -v-1H +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "namespace = \"prod\" AND type = \"Warning\"",
filters: "", sources: []
}' | groundcover k8s events-search --body-file /dev/stdinsh
END=$(date -u +%FT%TZ); START=$(date -u -v-1H +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "namespace = \"prod\" AND type = \"Warning\"",
filters: "", sources: []
}' | groundcover k8s events-search --body-file /dev/stdinFind slow traces, then pivot to their logs
查找慢链路追踪,然后关联到对应的日志
sh
END=$(date -u +%FT%TZ); START=$(date -u -v-30M +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "service_name = \"api\" AND duration_ms > 1000",
filters: "", sources: []
}' | groundcover traces search --body-file /dev/stdin \
| jq -r '.[] | .trace_id' | head -5 \
| while read tid; do
jq -n --arg s "$START" --arg e "$END" --arg t "$tid" '{
start:$s, end:$e, query: ("trace_id = \"" + $t + "\""), filters: "", sources: []
}' | groundcover logs search --body-file /dev/stdin
donesh
END=$(date -u +%FT%TZ); START=$(date -u -v-30M +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "service_name = \"api\" AND duration_ms > 1000",
filters: "", sources: []
}' | groundcover traces search --body-file /dev/stdin \
| jq -r '.[] | .trace_id' | head -5 \
| while read tid; do
jq -n --arg s "$START" --arg e "$END" --arg t "$tid" '{
start:$s, end:$e, query: ("trace_id = \"" + $t + "\""), filters: "", sources: []
}' | groundcover logs search --body-file /dev/stdin
doneDiscover what labels/fields exist on a stream
发现某数据流存在的标签/字段
sh
undefinedsh
undefinedWhat metric names match a prefix?
哪些指标名称匹配指定前缀?
jq -n '{ prefix: "http_" }' | groundcover metrics names --body-file /dev/stdin
jq -n '{ prefix: "http_" }' | groundcover metrics names --body-file /dev/stdin
What labels does a specific metric have?
某特定指标有哪些标签?
jq -n '{ metric: "http_request_duration_seconds" }' | groundcover metrics keys --body-file /dev/stdin
jq -n '{ metric: "http_request_duration_seconds" }' | groundcover metrics keys --body-file /dev/stdin
What values does a label take?
某标签有哪些取值?
jq -n '{ metric: "http_request_duration_seconds", key: "service" }'
| groundcover metrics values --body-file /dev/stdin
| groundcover metrics values --body-file /dev/stdin
undefinedjq -n '{ metric: "http_request_duration_seconds", key: "service" }'
| groundcover metrics values --body-file /dev/stdin
| groundcover metrics values --body-file /dev/stdin
undefinedGCQL quick reference (logs/traces/events queries)
GCQL快速参考(日志/链路追踪/事件查询)
- Equality: (double quotes inside JSON →
service_name = "api")\" - Comparison: ,
duration_ms > 1000status_code >= 500 - Boolean: ,
AND,ORNOT - Substring: , regex:
message ~ "timeout"message ~* "^panic:" - Membership: — works on a named field only
level IN ("error", "fatal") - Freetext phrase: a bare quoted string, e.g. .
container = "my-service" AND "database unavailable"is not valid as freetext (errorsIN(...)) — expand tofreetext 'in(...)' is not supported.level = "error" OR level = "fatal" - Never send an empty —
query→ 500query: "". Always supply at least one term.pipeline cannot be nil - Null check:
trace_id IS NOT NULL
Enumerating fields: / are finicky (discovery requires + and may still 400). The reliable move is to fetch one sample log and inspect and the parsed .
search discoverysearch keysLimitTypekeysbody- 相等判断:(JSON中需转义双引号为
service_name = "api")\" - 比较判断:、
duration_ms > 1000status_code >= 500 - 布尔运算:、
AND、ORNOT - 子字符串匹配:,正则匹配:
message ~ "timeout"message ~* "^panic:" - 成员判断:— 仅适用于命名字段
level IN ("error", "fatal") - 自由文本短语: 裸引号字符串,例如。
container = "my-service" AND "database unavailable"不能作为自由文本使用(会报错IN(...))——需展开为freetext 'in(...)' is not supported。level = "error" OR level = "fatal" - 切勿发送空—
query会返回500错误query: ""。请始终至少提供一个查询条件。pipeline cannot be nil - 空值检查:
trace_id IS NOT NULL
枚举字段:/操作较为繁琐(discovery需要 + 参数,仍可能返回400错误)。可靠的方法是获取一条日志样本并检查和解析后的字段。
search discoverysearch keysLimitTypekeysbodyFind then silence a monitor
查找并静默某监控器
sh
groundcover monitors list --query 'monitor_name ~ "ingest"'
groundcover silences create --body-json '{"monitor_id":"<id>","duration_seconds":3600,"reason":"deploy"}'sh
groundcover monitors list --query 'monitor_name ~ "ingest"'
groundcover silences create --body-json '{"monitor_id":"<id>","duration_seconds":3600,"reason":"deploy"}'Dump every dashboard
导出所有仪表板
The list field is , not ( is null). Each entry also has — skip archived ones (active = ).
uuidid.idarchivedTimestamp0001-01-01T00:00:00.000Zsh
groundcover dashboards list \
| jq -r '.[] | select(.archivedTimestamp == "0001-01-01T00:00:00.000Z") | .uuid' \
| while read id; do groundcover dashboards get "$id" > "dashboards/$id.json"; done列表字段为****,而非(为null)。每个条目还包含——跳过已归档的仪表板(活跃仪表板的该值为)。
uuidid.idarchivedTimestamp0001-01-01T00:00:00.000Zsh
groundcover dashboards list \
| jq -r '.[] | select(.archivedTimestamp == "0001-01-01T00:00:00.000Z") | .uuid' \
| while read id; do groundcover dashboards get "$id" > "dashboards/$id.json"; doneBuild a dashboard deep-link with pre-filled template variables
构建带预填充模板变量的仪表板深度链接
groundcover dashboards get <uuid>.presetfromjsonsh
groundcover dashboards list | jq -r '.[] | select(.name|test("prod";"i")) | "\(.uuid)\t\(.name)"'
groundcover dashboards get <uuid> | jq -r '.preset | fromjson | .variables'groundcover dashboards get <uuid>.presetfromjsonsh
groundcover dashboards list | jq -r '.[] | select(.name|test("prod";"i")) | "\(.uuid)\t\(.name)"'
groundcover dashboards get <uuid> | jq -r '.preset | fromjson | .variables'each var: { kind:"list", spec:{ values:{default:["*"]}, datasource:{key,kind,metric}, variableName } }
每个变量格式:{ kind:"list", spec:{ values:{default:["*"]}, datasource:{key,kind,metric}, variableName } }
The webapp encodes selected variable values in a `variables` **URL query param** — a URL-encoded JSON object keyed by `$<variableName>`. Each entry needs `name`, `key`, `values` (array; supports globs like `myapp*`), `datasource` (usually `"metrics"`), and `refiner.metric` (the source metric from the var's `datasource.metric`).
**Pin EVERY variable you care about — including ones you want wide open.** Any var you omit falls back to the dashboard's *saved default*, which is often NOT `*` (e.g. a hardcoded `deployment` hash, or `shard: primary`). To get a truly broad view, explicitly set those to `["*"]`. Always confirm a label value exists before using it — e.g. `app_kubernetes_io_instance` may have no bare `myapp`, only `myapp-web-0` etc., so use the `myapp*` glob.
Build it with jq so the encoding is correct:
```sh
VARS=$(jq -cn '{
"$cluster":{name:"cluster",key:"cluster",values:["example-cluster"],datasource:"metrics",refiner:{metric:"my_metric_total"}},
"$deployment":{name:"deployment",key:"deployment",values:["*"],datasource:"metrics",refiner:{metric:"my_metric_total"}},
"$app_kubernetes_io_instance":{name:"app_kubernetes_io_instance",key:"app_kubernetes_io_instance",values:["myapp*"],datasource:"metrics",refiner:{metric:"my_metric_total"}}
}')
ENC=$(jq -rn --arg v "$VARS" '$v|@uri')
echo "https://app.groundcover.com/dashboards?backendId=groundcover&tenantUUID=<your-tenant-uuid>&duration=Last+1+hour&viewId=<uuid>&qb-disable-auto-focus=true&variables=${ENC}"Notes: the dashboard opens via (not the path); takes UI strings like / .
viewId=<uuid>/dashboards/<uuid>durationLast+1+hourLast+5+minutesWeb应用会将选中的变量值编码到`variables`**URL查询参数**中——这是一个URL编码的JSON对象,键为`$<variableName>`。每个条目需要`name`、`key`、`values`(数组;支持通配符如`myapp*`)、`datasource`(通常为`"metrics"`)和`refiner.metric`(来自变量`datasource.metric`的源指标)。
**固定所有你关心的变量——包括那些你想要完全开放的变量。** 任何未指定的变量会回退到仪表板的**保存默认值**,而默认值通常不是`*`(例如硬编码的`deployment`哈希值,或`shard: primary`)。若要获得真正全面的视图,请显式将这些变量设置为`["*"]`。使用前请确认标签值存在——例如`app_kubernetes_io_instance`可能没有裸值`myapp`,只有`myapp-web-0`等,因此请使用`myapp*`通配符。
使用jq构建链接以确保编码正确:
```sh
VARS=$(jq -cn '{
"$cluster":{name:"cluster",key:"cluster",values:["example-cluster"],datasource:"metrics",refiner:{metric:"my_metric_total"}},
"$deployment":{name:"deployment",key:"deployment",values:["*"],datasource:"metrics",refiner:{metric:"my_metric_total"}},
"$app_kubernetes_io_instance":{name:"app_kubernetes_io_instance",key:"app_kubernetes_io_instance",values:["myapp*"],datasource:"metrics",refiner:{metric:"my_metric_total"}}
}')
ENC=$(jq -rn --arg v "$VARS" '$v|@uri')
echo "https://app.groundcover.com/dashboards?backendId=groundcover&tenantUUID=<your-tenant-uuid>&duration=Last+1+hour&viewId=<uuid>&qb-disable-auto-focus=true&variables=${ENC}"注意:仪表板通过打开(而非路径);接受UI字符串如 / 。
viewId=<uuid>/dashboards/<uuid>durationLast+1+hourLast+5+minutesAd-hoc Prom query via raw
通过原始命令执行临时Prom查询
sh
groundcover raw prometheus api query --query query='up{namespace="prod"}'sh
groundcover raw prometheus api query --query query='up{namespace="prod"}'Building & editing dashboards
构建与编辑仪表板
The interesting part of a dashboard isn't the CRUD verbs () — it's the preset, where the whole panel layout lives in one field.
dashboards get/list/create/update/archive/restore仪表板的核心并非CRUD操作()——而是preset字段,整个面板布局都存储在该字段中。
dashboards get/list/create/update/archive/restorePreset structure
Preset结构
dashboards get <uuid>.presetfromjsonjson.loadsjsonc
{
"spec": { "layoutType": "ordered", "crosshairSyncEnabled": true },
"layout": [ /* one entry per TOP-LEVEL item, placed on a 24-col grid */ ],
"widgets": [ /* the actual panels, referenced by id from layout */ ],
"duration": "Last 6 hours", // UI duration string
"variables": [ /* template vars, same shape as the deep-link recipe */ ],
"schemaVersion": 7
}- — top-level grid placement. Each entry
layout. Grid is 24 columns wide ({ "id":"A", "h":12, "w":24, "x":0, "y":0, "minH":8 }= a third,w:8= half);w:12must match a widgetid. You hand-computeidto stack rows — there is no auto-flow.y - —
widgets.{ id, name, type, queries:[{id, expr, dataType:"metrics", editorMode:"editor"}], visualizationConfig:{ type, selectedUnit, ... } }is PromQL withexprsubstitutions.$var - Widget types: ,
time-series,stat(markdown intext, no queries — use for headers/dividers),content.section
dashboards get <uuid>.presetfromjsonjson.loadsjsonc
{
"spec": { "layoutType": "ordered", "crosshairSyncEnabled": true },
"layout": [ /* 每个顶层条目,放置在24列网格上 */ ],
"widgets": [ /* 实际面板,通过id与layout关联 */ ],
"duration": "Last 6 hours", // UI时长字符串
"variables": [ /* 模板变量,与深度链接示例中的格式一致 */ ],
"schemaVersion": 7
}- — 顶层网格布局。每个条目格式为
layout。网格宽度为24列({ "id":"A", "h":12, "w":24, "x":0, "y":0, "minH":8 }表示三分之一宽度,w:8表示一半宽度);w:12必须与widget的id匹配。需手动计算id值来堆叠行——无自动流布局。y - — 格式为
widgets。{ id, name, type, queries:[{id, expr, dataType:"metrics", editorMode:"editor"}], visualizationConfig:{ type, selectedUnit, ... } }为带expr替换的PromQL表达式。$var - Widget类型:、
time-series、stat(text字段为markdown,无查询——用于标题/分隔符)、content。section
Sections (collapsible groups)
分组(可折叠)
A section is a widget . Its layout entry carries a array of relative-positioned panel entries: . The header occupies the top ~2 rows, so children start at .
{ id, name, type:"section", color, isCollapsed:false }children{ id, h, w:24, x:0, y:0, children:[ {h,w,x,y,id,minH}, ... ] }y:2分组是类型为的widget:。其layout条目包含数组,存储相对定位的面板条目:。标题占据顶部约2行,因此子项需从开始。
section{ id, name, type:"section", color, isCollapsed:false }children{ id, h, w:24, x:0, y:0, children:[ {h,w,x,y,id,minH}, ... ] }y:2The update contract
更新契约
dashboards update <uuid>currentRevision.revisionNumberField validation for 'CurrentRevision' failed on the 'required_if' taggetsh
groundcover dashboards get <uuid> > d.jsondashboards update <uuid>currentRevision.revisionNumberField validation for 'CurrentRevision' failed on the 'required_if' taggetsh
groundcover dashboards get <uuid> > d.jsonNEW_PRESET must be the preset object re-serialized to a STRING
NEW_PRESET必须是preset对象重新序列化为字符串后的结果
jq -n --slurpfile d d.json --arg preset "$NEW_PRESET" '{
name: $d[0].name, viewType: ($d[0].viewType // "explore"),
status: "active", currentRevision: $d[0].revisionNumber, preset: $preset
}' | groundcover dashboards update <uuid> --body-file /dev/stdin
undefinedjq -n --slurpfile d d.json --arg preset "$NEW_PRESET" '{
name: $d[0].name, viewType: ($d[0].viewType // "explore"),
status: "active", currentRevision: $d[0].revisionNumber, preset: $preset
}' | groundcover dashboards update <uuid> --body-file /dev/stdin
undefinedValidation rules (the API only says Dashboard validation failed
)
Dashboard validation failed验证规则(API仅返回Dashboard validation failed
)
Dashboard validation failedThe update endpoint returns an opaque — no field, no offending widget. When you hit it, bisect: deploy a minimal preset (one section / one panel) and add pieces back until it breaks. Two non-obvious rules that each cost real bisecting time:
400 {"message":"Dashboard validation failed"}- Stat widgets () validate ONLY as flat top-level
visualizationConfig.type:"stat"entries — NEVER inside a section'slayout. Time-series widgets work in both. To build an overview stat row, place the stat panels as flat top-level items (with achildren[]header widget above them) and keep time-series panels in sections.text - Section accepts only a fixed palette. Verified valid:
color,gray,purple. Verified rejected:teal,green,blue,red,yellow.orange
更新端点返回不透明的——不指明具体字段或有问题的widget。遇到该错误时,请二分排查:部署最小化preset(一个分组/一个面板),然后逐步添加内容直到出错。以下两个非直观规则均需花费大量时间排查:
400 {"message":"Dashboard validation failed"}- Stat类型widget()仅能作为顶层
visualizationConfig.type:"stat"条目——绝不能放在分组的layout中。 Time-series类型widget在两种场景下均可使用。若要构建概览统计行,请将stat面板作为顶层条目(上方放置children[]标题widget),将time-series面板放在分组中。text - 分组的仅接受固定调色板。 已验证有效的值:
color、gray、purple。已验证无效的值:teal、green、blue、red、yellow。orange
Stat panels read "No Results" on lagging metrics → wrap in last_over_time
last_over_time滞后指标导致Stat面板显示“No Results” → 使用last_over_time
包裹
last_over_timeStat panels evaluate as an instant query at "now". A metric that lags real-time leaves that instant window empty → the panel shows "No Results" even though it graphs fine in a time-series panel (which range-queries the whole window). Metrics pulled from polled integrations (e.g. cloud-provider integrations like CloudWatch) commonly lag minutes behind real-time, so stat panels built directly on them come up blank.
Fix: wrap the selector in inside the aggregation so the instant evaluation reaches back past the staleness:
last_over_time(<selector>[30m])promql
max(last_over_time(my_metric{...}[30m])) # NOT max(my_metric{...})The wrappers render fine in stat panels (e.g. a working uptime stat). In-cluster metrics scraped in real-time (~1s fresh) don't need it.
*_over_time100 * avg_over_time((...)[7d:5m])Stat面板会在“当前时间”执行即时查询。若指标存在实时滞后,该即时窗口会为空→面板显示**“No Results”**,即使该指标在time-series面板中可正常绘制(time-series面板会查询整个时间范围)。从轮询集成(如CloudWatch等云提供商集成)获取的指标通常会滞后数分钟,因此直接基于这些指标构建的Stat面板会显示空白。
修复方法:将选择器包裹在中,并放在聚合函数内部,以便即时查询能覆盖滞后时间:
last_over_time(<selector>[30m])promql
max(last_over_time(my_metric{...}[30m])) # 不要使用max(my_metric{...})*_over_time100 * avg_over_time((...)[7d:5m])Verifying a dashboard without the UI
无需UI即可验证仪表板
If the headless browser hits Groundcover's SSO wall (no session), you can't screenshot-verify. Verify via the API instead:
- Every query returns data: pull the deployed preset, substitute concrete values into each (a
expr→ a real label value; a wildcard var → a$varregex), and run it through=~".+"as a range query. Use a window ≥ the metric's emit cadence (sparse integration metrics may only emit every few minutes).metrics query - Geometry: assert no two layout rects overlap and every (top-level entries and each section's
x+w ≤ 24).children - Caveat: the CLI can't reliably run instant queries (see Common issues), so you can't reproduce the stat-panel instant path from the CLI — reason from staleness + range data.
若无头浏览器遇到Groundcover的SSO墙(无会话),无法通过截图验证。可通过API验证:
- 每个查询均返回数据: 获取已部署的preset,将具体值代入每个(
expr替换为实际标签值;通配符变量替换为$var正则),然后通过=~".+"执行范围查询。时间窗口需≥指标的发送频率(稀疏的集成指标可能仅每隔数分钟发送一次)。metrics query - 几何布局: 确保没有两个布局矩形重叠,且所有(顶层条目和每个分组的
x+w ≤ 24均需满足)。children - 注意: CLI无法可靠执行即时查询(见常见问题),因此无法从CLI复现Stat面板的即时查询路径——需根据滞后情况和范围数据进行推断。
Polled-integration metrics (e.g. CloudWatch): label-scheme gotchas
轮询集成指标(如CloudWatch):标签方案陷阱
Metrics ingested from a polled cloud integration differ from in-cluster exporter metrics in ways that bite when building dashboards:
- They lag real-time (see the stat-panel note above) — minutes, not seconds.
- They're often multiplied by a dimension (
stat) — every series appears 3×. Always filter to the one you want (e.g.stat=average/maximum/minimum) or aggregations double/triple-count.stat="average" - The env/scope label is integration-named, not a clean . If the same resource is also covered by an in-cluster exporter, the two families carry different label keys for the same concept (e.g.
envvsinstance_id). Use a separate template variable per label scheme rather than forcing one variable to span both.instanceid
从轮询云集成摄入的指标与集群内导出器指标存在差异,构建仪表板时需注意:
- 存在实时滞后(见Stat面板说明)——数分钟而非数秒。
- 通常带有维度(
stat)——每个时间序列会出现3次。请始终过滤到所需的维度(如stat=average/maximum/minimum),否则聚合结果会重复计数。stat="average" - 环境/范围标签为集成特定名称,而非简洁的。若同一资源同时被集群内导出器覆盖,两个指标系列的标签键会不同(如
envvsinstance_id)。请为每种标签方案使用单独的模板变量,而非强制使用一个变量覆盖两种场景。instanceid
Production observability triage flow
生产环境可观测性排查流程
When asked "why is X broken in prod" / "what's going on with service Y":
- Scope the time window. Default to last 1h. Compute /
STARTwithEND(see recipes).date -u - Logs first. with a GCQL filter on
groundcover logs searchandservice_name. Cheap and usually answers it.level = "error" - Traces if latency / slow. filtering on
groundcover traces search,duration_ms, orstatus_code. Grab a fewservice_names and pivot back to logs viatrace_id.trace_id = "..." - Metrics for shape over time. with PromQL — error rate, p99, saturation. Use
groundcover metrics queryfor graphs,rangefor a point check.instant - K8s if "is it even running". for status/restarts;
groundcover k8s workloadsforgroundcover k8s events-searchevents (OOMKilled, FailedScheduling, BackOff).Warning - Don't know the field name? ,
groundcover search discovery,metrics names,metrics keysto enumerate.metrics values - Need to silence noisy alerts during the incident? →
groundcover monitors list --query ….groundcover silences create
当被问及“为什么X在生产环境故障” / “服务Y出现了什么问题”时:
- 确定时间范围。 默认最近1小时。使用计算
date -u/START(见示例)。END - 优先查看日志。 使用,通过GCQL过滤
groundcover logs search和service_name。成本低且通常能直接找到问题原因。level = "error" - 若涉及延迟/慢请求,查看链路追踪。 使用过滤
groundcover traces search、duration_ms或status_code。获取几个service_name并通过trace_id关联回日志。trace_id = "..." - 查看指标了解时间趋势。 使用执行PromQL查询——错误率、p99延迟、饱和度。使用
groundcover metrics query查询获取图表,使用range查询获取单点数据。instant - 若怀疑“服务是否运行”,查看K8s状态。 使用查看状态/重启次数;使用
groundcover k8s workloads查看groundcover k8s events-search事件(OOMKilled、FailedScheduling、BackOff)。Warning - 不知道字段名称? 使用、
groundcover search discovery、metrics names、metrics keys枚举字段。metrics values - 事件期间需要静默嘈杂的告警? 使用查找监控器 → 使用
groundcover monitors list --query …创建静默规则。groundcover silences create
When unsure which command exists
不确定命令是否存在时
- Check the resource list above.
- and
groundcover --help.groundcover <resource> --help - For raw: , then
groundcover raw list.groundcover raw list <group>
- 查看上文的资源列表。
- 执行和
groundcover --help。groundcover <resource> --help - 对于原始命令:执行,然后执行
groundcover raw list。groundcover raw list <group>
Common issues
常见问题
401 Unauthorized
, token-prefix/length errors, or empty results
401 Unauthorized401 Unauthorized
、令牌前缀/长度错误或空结果
401 Unauthorized- /
Invalid token prefix→ the key isn't aInvalid token lengthservice-account key (see Auth). Agcsa_ingestion key is the common mistake.gcik_ - with a valid-looking
401key → the key is for a different tenant. For other tenants also setgcsa_and (if applicable)GROUNDCOVER_TENANT_UUID.GROUNDCOVER_BASE_URL - Otherwise verify the env var is actually exported in the current shell.
- /
Invalid token prefix→ 密钥不是Invalid token length服务账号密钥(见认证章节)。常见错误是使用gcsa_数据摄入密钥。gcik_ - 格式正确的密钥返回
gcsa_→ 密钥所属的租户与当前查询的租户不一致。访问其他租户时还需设置401和(若适用)GROUNDCOVER_TENANT_UUID。GROUNDCOVER_BASE_URL - 否则请确认环境变量已在当前shell中正确导出。
SDK command rejects the body
SDK命令拒绝请求体
Body shape drifted from the SDK contract. Fetch an existing resource with and use its shape as a template.
get <id>请求体格式与SDK契约不一致。使用获取现有资源,并以其格式为模板。
get <id>Empty results from logs search
/ traces search
/ events-search
logs searchtraces searchevents-searchlogs search
/ traces search
/ events-search
返回空结果
logs searchtraces searchevents-searchAlmost always one of:
- Time window is wrong / too narrow. must be before
start, both RFC 3339 UTC.end - GCQL field name doesn't exist in this tenant. Use /
search discoveryto enumerate.search keys - Query string isn't escaped — inside JSON, becomes
service_name = "api"."service_name = \"api\""
几乎总是以下原因之一:
- 时间范围错误/过窄。必须早于
start,且均为RFC 3339 UTC格式。end - GCQL字段名称在当前租户中不存在。使用/
search discovery枚举字段。search keys - 查询字符串未转义——在JSON中,需写为
service_name = "api"。"service_name = \"api\""
metrics query
returns no data but the metric exists
metrics querymetrics query
返回空数据但指标存在
metrics query- Field names are capitalized (,
Promql,Start,End,Step) — lowercase is silently accepted by JSON but ignored.QueryType - is a string (
Step), not a number."60" - For instant queries set and omit
"QueryType": "instant". Observed caveat: in testing the CLIStephas returnedmetrics queryfor instant bodies (every shape tried —400 metricsQueryBadRequest==Start,End, with/withoutTime), even on fresh metrics. When you need a single current value, fall back to a range query (QueryType+QueryType:"range") over a short window and take the last point. (Genuine instant PromQL is also reachable viaStep, but that passthrough may target a different store thanraw prometheus api query.)metrics query - Parsing the response: results live at ; each
.data.result // .resultentry isvalues. Sum a range with[unix_ts, "stringvalue"]; format the timestamp with[.values[][1]] | map(tonumber) | add.strftime
- 字段名称首字母大写(、
Promql、Start、End、Step)——小写会被JSON接受但被忽略。QueryType - 为字符串(
Step),而非数字。"60" - 即时查询需设置并省略
"QueryType": "instant"。已知问题: 测试中发现CLI的Step对即时请求体返回metrics query(尝试过所有格式——400 metricsQueryBadRequest==Start、End、带/不带Time),即使是最新指标。若需要当前单点值,请回退到范围查询(QueryType+QueryType:"range"),查询短时间窗口并取最后一个数据点。(真正的即时PromQL也可通过Step执行,但该透传可能指向与raw prometheus api query不同的存储。)metrics query - 响应解析:结果位于;每个
.data.result // .result条目格式为values。求和范围数据可使用[unix_ts, "stringvalue"];使用[.values[][1]] | map(tonumber) | add格式化时间戳。strftime
logs search
times out (context deadline exceeded
)
logs searchcontext deadline exceededlogs search
超时(context deadline exceeded
)
logs searchcontext deadline exceededChatty, high-volume workloads flood logs, so broad or single-pod queries over even a ~60–90s window can exceed the deadline. Fixes:
- Always include a discriminating term (a phrase + /
container), not just a pod by itself.pod - Narrow the window and raise (e.g.
--timeout).--timeout 120s - Avoid large chains in one query — run them separately.
OR - For "how often / over time" questions prefer over scraping logs.
metrics query
高流量的嘈杂工作负载会产生大量日志,因此即使是约60–90秒的宽范围或单Pod查询也可能超过超时时间。修复方法:
- 始终包含区分性条件(短语 + /
container),而非仅指定Pod。pod - 缩小时间范围并增加(如
--timeout)。--timeout 120s - 避免在单个查询中使用大型链——分开执行查询。
OR - 对于“频率/时间趋势”类问题,优先使用****而非日志查询。
metrics query
monitors
editing & search
monitorsmonitors
编辑与搜索
monitors- /
monitors getspeak YAML (even withupdate). Workflow:--raw, edit,monitors get <uuid> > mon.yaml. A successful update returnsmonitors update <uuid> --body-file mon.yaml(applied asynchronously — re-{"status":202}to confirm).get - Monitor model lives under — each condition is
model.queries[].sqlPipeline.filters.conditions[]; freetext uses{key, origin: root, type, filters:[{op, value}]}+type: freetext. Other knobs:op: phrase_search(e.g.instantRollup),1 minute,thresholds[].evaluationInterval.{interval, pendingFor} - → 400
monitors list --query 'monitor_name ~ "..."'. Useregexp requires a string column type, or list all and filter withmonitor_name = "exact".jq
- /
monitors get使用YAML格式(即使使用update)。工作流:--raw,编辑文件,monitors get <uuid> > mon.yaml。更新成功后返回monitors update <uuid> --body-file mon.yaml(异步应用——需重新执行{"status":202}确认)。get - 监控器模型位于——每个条件格式为
model.queries[].sqlPipeline.filters.conditions[];自由文本使用{key, origin: root, type, filters:[{op, value}]}+type: freetext。其他配置项:op: phrase_search(如instantRollup)、1 minute、thresholds[]。evaluationInterval.{interval, pendingFor} - → 返回400错误
monitors list --query 'monitor_name ~ "..."'。请使用regexp requires a string column type,或列出所有监控器并使用monitor_name = "exact"过滤。jq
Raw command "not found" inside a group
分组内的原始命令“未找到”
The endpoint isn't in the captured HAR. Use the SDK form if one exists, or regenerate — regeneration lives in the repo ().
groundcover-cligo run ./scripts/generate-commands.go <har>该端点未被捕获到HAR中。若存在SDK命令则使用SDK方式,或重新生成——生成逻辑位于仓库()。
groundcover-cligo run ./scripts/generate-commands.go <har>