feat(telemetry): bridge configurable gateway trace headers

This commit is contained in:
2026-10-06 21:21:35 +08:00
parent d65af03162
commit 9665fb5974
20 changed files with 484 additions and 25 deletions

View File

@@ -1,11 +1,11 @@
# API 与通用工程混合工作区
本例只定义生成工程的类型与目录,不包含业务模型、外部服务或特定调度方式。使用 v0.29.0-preview 或更新版本 CLI。
本例只定义生成工程的类型与目录,不包含业务模型、外部服务或特定调度方式。使用 v0.30.0-preview 或更新版本 CLI。
下面从一个空目录创建两个独立 Go module;API 工程来自 API 模板,通用工程默认不监听端口。
```sh
go install git.yuntuo.hk/k2safe/go-omni/cmd/go-omni@v0.29.0-preview
go install git.yuntuo.hk/k2safe/go-omni/cmd/go-omni@v0.30.0-preview
mkdir mixed-demo
cd mixed-demo
go-omni api new apps/interface --module example.com/interface

View File

@@ -5,7 +5,7 @@
在 go-omni 仓库根目录执行(新建独立临时目录):
```sh
go install git.yuntuo.hk/k2safe/go-omni/cmd/go-omni@v0.29.0-preview
go install git.yuntuo.hk/k2safe/go-omni/cmd/go-omni@v0.30.0-preview
example_dir="$(mktemp -d)/recursive"
go-omni api generate --api examples/recursiveResponse/service.api --dir "$example_dir" --module example.com/recursive
# 以下文件仅是本次新生成示例的占位实现,替换为示例自身维护的逻辑。

View File

@@ -0,0 +1,11 @@
# 配置化网关 Trace Header
```sh
go run ./examples/telemetryHeaders
```
这个离线示例使用标准库 `httptest`,不连接网关、数据库或外部 Collector。它把 `X-Trace-Id`、`X-Span-Id` 和 `X-Request-Id` 配置给通用 `telemetry.HeaderPropagator`,再通过 `app.Use(runtime.Middleware())` 进入服务。业务 Handler 只读取 `logx.TraceID` / `logx.RequestID`,不解析 Header。
`HeaderPropagator` 同时支持标准 W3C `traceparent`;两种格式同时存在时优先 W3C。出站请求使用 `telemetry.Transport` 时会注入 W3C 和配置的兼容 Header。旧项目不配置它时,go-omni 仍只使用默认 W3C 传播。
示例中的 ID 是固定测试值。生产环境只有在网关或受信代理已经校验这些 Header 时才启用兼容传播;Trace ID/Span ID 不是认证凭据,也不能用于授权、幂等或租户隔离。自定义 Header 不携带采样标志,适配器会把有效的成对 ID 视为已采样;需要保留上游采样决定时应使用 `traceparent`。

View File

@@ -0,0 +1,62 @@
// telemetryHeaders demonstrates a provider-neutral compatibility boundary for
// gateways that carry trace IDs in configurable HTTP headers.
package main
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
omni "git.yuntuo.hk/k2safe/go-omni"
"git.yuntuo.hk/k2safe/go-omni/logx"
"git.yuntuo.hk/k2safe/go-omni/telemetry"
)
func main() {
if err := run(); err != nil {
panic(err)
}
}
func run() error {
propagator, err := telemetry.NewHeaderPropagator(telemetry.HeaderPropagationConfig{
TraceIDHeader: "X-Trace-Id", SpanIDHeader: "X-Span-Id", RequestIDHeader: "X-Request-Id",
})
if err != nil {
return err
}
runtime, err := telemetry.New(telemetry.Config{Service: "header-example", SampleRatio: 1, Propagator: propagator})
if err != nil {
return err
}
defer runtime.Close()
app := omni.New(omni.WithLogger(false), omni.WithRequestIDHeader("X-Request-Id"))
defer app.Close()
app.Use(runtime.Middleware())
app.GET("/", func(c *omni.Context) error {
fmt.Printf("traceId=%s requestId=%s\n", logx.TraceID(c.Context()), logx.RequestID(c.Context()))
return c.OK(map[string]string{"traceId": logx.TraceID(c.Context()), "requestId": logx.RequestID(c.Context())})
})
server := httptest.NewServer(app)
defer server.Close()
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil)
if err != nil {
return err
}
req.Header.Set("X-Trace-Id", "11111111111111111111111111111111")
req.Header.Set("X-Span-Id", "2222222222222222")
req.Header.Set("X-Request-Id", "example-request")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Printf("status=%d response=%s", resp.StatusCode, body)
return nil
}

View File

@@ -0,0 +1,9 @@
package main
import "testing"
func TestTelemetryHeadersExample(t *testing.T) {
if err := run(); err != nil {
t.Fatal(err)
}
}