Skip to content

Observability

Observability 用日志回答“某一次请求发生了什么”,用 metrics 回答“系统整体发生了多少次、耗时如何、当前状态如何”。两类信号共享产品语义,但不共享完整字段集:日志可以携带单次请求的关联信息,metrics label 必须控制 cardinality。

状态边界

当前代码已经支持:

  • 进程级 slog,默认写入 stderr,并可选 fan-out 到 Volc TLS;
  • GizClaw HTTP 与 Peer RPC 的单次结构化 completion log;
  • 进程级 gizmetrics counter、gauge、histogram recorder,以及可复用的 net/http metrics wrapper;
  • Admin HTTP GET /logs/stream,从已配置的日志 backend 流式查询日志;
  • pkgs/store/metrics.Store,通过 Prometheus Remote Write 写入并通过 Prometheus HTTP API 查询;
  • Peer telemetry 的 battery、GNSS、network 和 system metrics。

信号和 ownership

Ownership状态
cmd/internal/logging安装全局 slog、配置 level、fan-out、stderr 和 Volc TLS sink当前已有
pkgs/gizclaw/internal/observabilityGizClaw 的 transport、surface、operation、result、error 和安全字段 vocabulary,以及到 slog 的 projection当前已有
pkgs/gizmetrics进程级 counter、gauge、histogram、聚合、批量 flush 和 no-op default当前已有
pkgs/gizmetrics/httpmetrics通用 net/http request count、duration、in-flight 和 response bytes wrapper当前已有
pkgs/store/metrics数值 sample 的持久化与查询 backend,不拥有业务 metric name 或 label当前已有
services/runtime/peertelemetryPeer telemetry packet 到 metric name、peer_id label 和数值的映射当前已有

GenX stream、Transformer 的 EOS/cancel/backpressure 指标由 pkgs/genx 的 wrapper 拥有;WebRTC connection、ICE、DataChannel、packet loss 和 RTT 指标由 pkgs/giznet/gizwebrtc 的 observer 拥有。通用 metrics package 不反向依赖这些业务或 transport package。

统一请求维度

日志和进程请求 metrics 使用同一组有界语义:

维度值或来源说明
transporthttprpcWebRTC signaling 是 HTTP operation,不是独立 transport。
surfaceserver-publicpeer-httpadmin-httppeer-openaiedge-httppeer-rpc表示请求从哪个 GizClaw ingress surface 进入。
operationOpenAPI operation ID、RPC method 或显式注册的常量必须有界;无法识别时使用 unknown,不能回退到 raw path。
methodHTTP method只使用标准 method,不包含 URL;其他值归一为 OTHER
resultsuccessclient_errorserver_errorcanceledpanictransport_error表示完成结果,不替代 HTTP/RPC code。
status_class2xx3xx4xx5xxunknown用于聚合;日志仍保留精确 statusrpc_code

这些字段是产品 taxonomy。Sink、Prometheus backend 和调用方不能自行创造同义值,例如不能把 peer-http 同时作为 transportsurface

结构化日志

输出格式

代码继续直接使用全局 slog,优先通过 slog.LogAttrs(ctx, ...) 输出 scalar attributes。Volc TLS handler 将 levelmsg 和每个 scalar attribute 保存为独立字段;StreamServerLogs 再规范化为:

返回字段含义
time_ms / time_nsbackend 提供的日志时间;time_ns 可选。
level规范化后的日志 level。
messageslog.Record.Message,来自 backend 的 msg
source当前 Volc sink 写入 gizclaw
path当前 Volc sink 写入 slog
fields除保留字段之外的结构化 scalar attributes。

请求 completion record 使用稳定 message gizclaw: request completed。HTTP handler 每次完成输出一次;Peer RPC 在第一帧开始后输出一次,连接在新请求首帧之前正常 EOF 时不输出:

AttributeHTTPRPC可用于定位Metrics label
transport协议低基数,可用
surfaceingress低基数,可用
operationhandler / RPC method低基数,可用
result完成分类低基数,可用
status_class聚合状态低基数,可用
duration_ms单次耗时不作为 label;使用 histogram value
method / route / statusHTTP 请求与精确状态methodstatus_class 可作为通用 HTTP labels
rpc_code响应包含 code 时JSON-RPC 或应用 code不直接作为通用 label
error_code失败时失败时稳定领域错误只有封闭且有界的 code 集合才可作为产品 metric label
request_id单次请求关联禁止作为 label
peer_public_key / peer_role已认证且已知时已认证且已知时调用方身份禁止作为进程请求 metric label
workspace_nameworkflow_namemodel_idresource_kindresource_name已安全解析且需要时已安全解析且需要时领域上下文禁止作为进程请求 metric label

格式示例:

text
time=2026-07-16T10:00:00Z level=WARN msg="gizclaw: request completed" transport=rpc surface=peer-rpc operation=server.workspace.create result=client_error status_class=4xx rpc_code=400 error_code=INVALID_WORKSPACE request_id=req-01 duration_ms=12

Level

Level请求结果
INFO普通 2xx/3xx completion。
WARNHTTP 4xx、RPC bad request/forbidden/not found/conflict、JSON-RPC parse/invalid request/invalid params/method not found,以及取消。
ERRORHTTP 5xx、JSON-RPC internal error、panic 和 transport failure。

Streaming RPC 只在完整 stream handler 返回时输出一次 completion record;不输出 per-frame、audio、event payload 或成功 chunk 日志。

筛选

GET /logs/streamfilter 使用 GizClaw-owned grammar,不接受 backend-native query。Filter 为 *,或最多 32 个 uppercase AND 连接的 clause;支持 level:valuetext:valuefield:valuefield!=valuefield:*-field:*。例如:

text
level:ERROR
surface:peer-rpc
operation:"server.workspace.create"
error_code:INVALID_WORKSPACE
request_id:req-01

Value 是不含 whitespace、quote、backslash 或 wildcard 的 token,或不含 wildcard 的 JSON string literal。标准 level 名称会归一化为 uppercase。Field 使用 LogStore dotted-attribute grammar;messagestreamkind 和 provider metadata/time field 保留。不接受 OR、regex、provider function 或 raw provider expression。Filter 最长 4096 bytes,field 最长 128 bytes,decoded value 最长 1024 bytes。请求 completion fields 落地并建立索引后,Grafana 与 Admin log query 都应直接按 scalar field 筛选,不解析 message

首次查询必须提供 inclusive start_time_ms 和 exclusive end_time_mslimit 默认 100、最大 1000,orderascdesc。下一页使用 end event 返回的 opaque cursor;带 cursor 继续查询时不能改变 filter、时间范围或 order。

敏感信息

日志不得包含 Authorization、cookie、signature、nonce、private key、credential、access key、请求/响应 body、SDP、audio、image、file、prompt、conversation、workflow event、raw URL/query、provider error text 或任意 panic value。

Completion record 不输出 error_message。响应 message、err.Error()、validation/provider text 和 panic value 均不会投影到结构化字段;peer_public_key 只记录已经用于 authorization 的认证身份。

Metrics

写入与查询路径

pkgs/store/metrics.Store 接收带 name、labels、timestamp 和 value 的 sample。Prometheus backend 使用 Remote Write 写入,通过 /api/v1/query/api/v1/query_range 查询;项目不使用 Pushgateway,也不提供 /metrics scrape endpoint。

当前 Peer telemetry 直接在带 timeout 的上下文中调用 Store.Append。进程 metrics runtime 则先在内存中聚合 counter、gauge 和 histogram,再按 batch flush,避免在 HTTP 业务路径执行 Remote Write。未配置名为 metrics 的 store 时不安装 recorder,埋点调用保持 no-op,不创建隐式 memory store。

进程级 recorder

gizmetrics Go API Reference · httpmetrics Go API Reference

调用方通过 AddCounterSetGaugeObserveHistogram 记录数据。InstallStore 一次只允许安装一个 live recorder;安装前和 shutdown 后调用均为 no-op。默认 flush interval 是 10 秒,单次 append timeout 是 5 秒,逻辑 series 上限是 10,000,也可通过 WithFlushIntervalWithAppendTimeoutWithMaxSeries 调整。

Counter 保存进程内单调累计值,gauge 保存最新值,histogram 导出累计的 _bucket_sum_count samples,并总是包含 le=+Inf。Metric name、label name、数值和 buckets 在进入聚合 map 前校验;非法 update、同一 series 改变类型/buckets 或超过 series 上限时丢弃并输出限频且不包含 label value 的 warning。业务调用不等待 Store.Append,失败或超时的 dirty samples 留待下一次 flush。

cmd/internal/server 只在配置了名为 metrics 的 store 时安装 recorder。关闭顺序固定为 gizclaw.Server、recorder final flush、store registry;recorder 不关闭 store。

当前 Peer telemetry metrics

当前所有 Peer telemetry series 只有 peer_id label。它是设备查询的显式 identity 维度,不应复制到通用 HTTP/RPC 请求 metrics。

Metric含义单位或取值
gizclaw_peer_battery_percent电池电量0-100 percent
gizclaw_peer_battery_charging是否充电0 或 1
gizclaw_peer_battery_voltage_mv电池电压millivolt
gizclaw_peer_gnss_latitude纬度degree
gizclaw_peer_gnss_longitude经度degree
gizclaw_peer_gnss_altitude_m海拔meter
gizclaw_peer_gnss_accuracy_m定位精度meter
gizclaw_peer_network_rssi_dbm网络 RSSIdBm
gizclaw_peer_network_signal_level设备报告的信号等级原始数值
gizclaw_peer_network_connected是否联网0 或 1
gizclaw_peer_system_uptime_seconds系统运行时间second
gizclaw_peer_system_free_memory_bytes可用内存byte
gizclaw_peer_system_temperature_c系统温度Celsius

查询示例:

text
gizclaw_peer_battery_percent{peer_id="<public-key>"}
last_over_time(gizclaw_peer_system_temperature_c{peer_id="<public-key>"}[5m])

统一 HTTP server metrics

通用 HTTP wrapper 定义以下 metric families:

Metric类型Labels
giz_http_server_requests_totalCountersurface, operation, method, status_class, result
giz_http_server_request_duration_secondsHistogramsurface, operation, method, status_class, result, exporter 增加的 le
giz_http_server_requests_in_flightGaugesurface, operation, method
giz_http_server_response_bytes_totalCountersurface, operation, method, status_class, result

Duration buckets 是 0.0050.010.0250.050.10.250.512.5510 seconds。

method 只保留 GETHEADPOSTPUTPATCHDELETEOPTIONSCONNECTTRACE,其他值归一为 OTHER。In-flight gauge 在同一进程的多个 wrapper 实例之间按相同 label set 聚合。Wrapper 保留底层 writer 已支持的 http.Flusherhttp.Hijackerio.ReaderFromhttp.Pusher,记录 panic 后继续抛出,不改变 recovery policy。

httpmetrics.Wrap 是可复用测量能力,并不会自动给所有 GizClaw surface 增加 request metrics。具体产品 operation 的接入需要由 owner package 显式提供稳定 resolver。Peer RPC、GenX 和 WebRTC metrics 不由这个 HTTP wrapper 采集。

聚合示例:

text
sum by (surface, operation, status_class) (
  rate(giz_http_server_requests_total[5m])
)

histogram_quantile(
  0.95,
  sum by (le, surface, operation) (
    rate(giz_http_server_request_duration_seconds_bucket[5m])
  )
)

Label cardinality

进程请求 metrics 只接受有限枚举或注册表中的值。禁止使用 raw URL/path/query、request ID、peer public key、workspace/workflow/model/resource identifier、credential/provider message、error message、prompt 或其他用户内容作为 label。

operation 必须来自 generated operation ID、RPC method 或显式注册常量;未识别时使用 unknown。如果某个 error_code 来自开放文本或 provider,不能成为 label;只有 server-owned、封闭且有界的 code 集合才能进入产品 metric。

新增埋点时

  1. 先判断问题需要单次请求证据、聚合趋势,还是两者都需要。
  2. 从统一 taxonomy 选择 transportsurfaceoperationresult,不要创建同义字段。
  3. 日志保留诊断所需的安全关联信息;metrics 只保留低 cardinality labels。
  4. HTTP 通用测量放在 pkgs/gizmetrics/httpmetrics,GizClaw 产品字段放在 pkgs/gizclaw/internal/observability,GenX 与 WebRTC 指标留在各自 owner package。
  5. 测试成功、4xx/5xx、取消、panic、streaming、backend failure、redaction 和 no-store 路径,并证明 instrumentation 不改变业务 response 或 lifecycle。