在链路跟踪中,TraceID
作为在各个服务间传递的唯一标识,用于串联服务间请求关联关系,是非常重要的一项数据。TraceID
是通过Context
参数传递的,如果使用框架的glog
日志组件,那么在日志打印中将会自动读取TraceID
记录到日志内容中。因此也建议大家使用框架的glog
日志组件来打印日志,便于完美地支持链路跟踪特性。
如果使用GoFrame
框架的Client
,那么所有的请求将会自带TraceID
的注入。GoFrame
的TraceID
使用的是OpenTelemetry
规范,是由十六进制字符组成的的32
字节字符串。
强烈建议大家统一使用 |
如果使用GoFrame
框架的Server
,如果请求带有TraceID
,那么将会自动承接到Context
中;否则,将会自动注入标准的TraceID
,并传递给后续逻辑。
如果使用GoFrame
框架的Client
,这里有三种方式。
通过gctx.New/WithCtx
方法创建一个带有TraceID
的Context
,该Context
参数用于传递给请求方法。随后可以通过gctx.CtxId
方法获取整个链路的TraceID
。相关方法:
// New creates and returns a context which contains context id. func New() context.Context // WithCtx creates and returns a context containing context id upon given parent context `ctx`. func WithCtx(ctx context.Context) context.Context |
使用WithCtx
方法时,如果给定的ctx
参数本身已经带有TraceID
,那么它将会直接使用该TraceID
,并不会新建。
这里还有个高级的用法,客户端可以自定义TraceID
,使用gtrace.WithTraceID
方法。方法定义如下:
// WithTraceID injects custom trace id into context to propagate. func WithTraceID(ctx context.Context, traceID string) (context.Context, error) |
如果是请求 GoFrame
框架的 Server
,那么在返回请求的 Header
中将会增加 Trace-Id
字段,供客户端读取。
如果使用GoFrame
框架的Server
,直接通过gctx.CtxId
方法即可获取TraceID
。相关方法:
// CtxId retrieves and returns the context id from context. func CtxId(ctx context.Context) string |
package main import ( "context" "time" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gctx" ) func main() { s := g.Server() s.BindHandler("/", func(r *ghttp.Request) { traceID := gctx.CtxId(r.Context()) g.Log().Info(r.Context(), "handler") r.Response.Write(traceID) }) s.SetPort(8199) go s.Start() time.Sleep(time.Second) req, err := g.Client().Get(context.Background(), "http://127.0.0.1:8199/") if err != nil { panic(err) } defer req.Close() req.RawDump() } |
执行后,终端输出:
ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE ----------|--------|-------|-----------------------------------------------------------------|-------------------- :8199 | ALL | / | main.main.func1 | ----------|--------|-------|-----------------------------------------------------------------|-------------------- :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE ----------|--------|-------|-----------------------------------------------------------------|-------------------- 2022-06-06 21:14:37.245 [INFO] pid[55899]: http server started listening on [:8199] 2022-06-06 21:14:38.247 [INFO] {908d2027560af616e218e912d2ac3972} handler +---------------------------------------------+ | REQUEST | +---------------------------------------------+ GET / HTTP/1.1 Host: 127.0.0.1:8199 User-Agent: GClient v2.1.0-rc4 at TXQIANGGUO-MB0 Traceparent: 00-908d2027560af616e218e912d2ac3972-1e291041b9afa718-01 Accept-Encoding: gzip +---------------------------------------------+ | RESPONSE | +---------------------------------------------+ HTTP/1.1 200 OK Connection: close Content-Length: 32 Content-Type: text/plain; charset=utf-8 Date: Mon, 06 Jun 2022 13:14:38 GMT Server: GoFrame HTTP Server Trace-Id: 908d2027560af616e218e912d2ac3972 908d2027560af616e218e912d2ac3972 |
TraceID
package main import ( "time" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gctx" ) func main() { s := g.Server() s.BindHandler("/", func(r *ghttp.Request) { traceID := gctx.CtxId(r.Context()) g.Log().Info(r.Context(), "handler") r.Response.Write(traceID) }) s.SetPort(8199) go s.Start() time.Sleep(time.Second) ctx := gctx.New() g.Log().Info(ctx, "request starts") content := g.Client().GetContent(ctx, "http://127.0.0.1:8199/") g.Log().Infof(ctx, "response: %s", content) } |
执行后,终端输出:
2022-06-06 21:17:17.447 [INFO] pid[56070]: http server started listening on [:8199] ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE ----------|--------|-------|-----------------------------------------------------------------|-------------------- :8199 | ALL | / | main.main.func1 | ----------|--------|-------|-----------------------------------------------------------------|-------------------- :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE ----------|--------|-------|-----------------------------------------------------------------|-------------------- 2022-06-06 21:17:18.450 [INFO] {e843f0737b0af616d8ed185d46ba65c5} request starts 2022-06-06 21:17:18.451 [INFO] {e843f0737b0af616d8ed185d46ba65c5} handler 2022-06-06 21:17:18.451 [INFO] {e843f0737b0af616d8ed185d46ba65c5} response: e843f0737b0af616d8ed185d46ba65c5 |
TraceID
package main import ( "context" "time" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/gtrace" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/text/gstr" ) func main() { s := g.Server() s.BindHandler("/", func(r *ghttp.Request) { traceID := gctx.CtxId(r.Context()) g.Log().Info(r.Context(), "handler") r.Response.Write(traceID) }) s.SetPort(8199) go s.Start() time.Sleep(time.Second) ctx, _ := gtrace.WithTraceID(context.Background(), gstr.Repeat("a", 32)) g.Log().Info(ctx, "request starts") content := g.Client().GetContent(ctx, "http://127.0.0.1:8199/") g.Log().Infof(ctx, "response: %s", content) } |
执行后,终端输出:
ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE ----------|--------|-------|-----------------------------------------------------------------|-------------------- :8199 | ALL | / | main.main.func1 | ----------|--------|-------|-----------------------------------------------------------------|-------------------- :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE ----------|--------|-------|-----------------------------------------------------------------|-------------------- 2022-06-06 21:40:03.897 [INFO] pid[58435]: http server started listening on [:8199] 2022-06-06 21:40:04.900 [INFO] {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} request starts 2022-06-06 21:40:04.901 [INFO] {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} handler 2022-06-06 21:40:04.901 [INFO] {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} response: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
GoFrame
框架的Client/Server
,如何获取链路的TraceID
?可以参考GoFrame
框架的Client/Server
的链路跟踪实现,并自行实现一遍,这块可能需要一定成本。
如果使用的第三方Client/Server
组件,请参考第三方组件相关介绍。
OpenTelemetry
规范,但是每个请求都带RequestID
参数,形如 33612a70-990a-11ea-87fe-fd68517e7a2d
,如何和TraceID
结合起来?根据我的分析,你这个RequestID
的格式和TraceID
规范非常切合,使用的是UUID
字符串,而UUID
可直接转换为TraceID
。建议在自己的Server
内部第一层中间件中将RequestID
转换为TraceID
,通过自定义TraceID
的方式注入到Context
中,并将该Context
传递给后续业务逻辑。
我来给你写个例子吧:
package main import ( "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/gtrace" "github.com/gogf/gf/v2/os/gctx" ) func main() { // 内部服务 internalServer := g.Server("internalServer") internalServer.BindHandler("/", func(r *ghttp.Request) { traceID := gctx.CtxId(r.Context()) g.Log().Info(r.Context(), "internalServer handler") r.Response.Write(traceID) }) internalServer.SetPort(8199) go internalServer.Start() // 外部服务,访问以测试 // http://127.0.0.1:8299/?RequestID=33612a70-990a-11ea-87fe-fd68517e7a2d userSideServer := g.Server("userSideServer") userSideServer.Use(func(r *ghttp.Request) { requestID := r.Get("RequestID").String() if requestID != "" { newCtx, err := gtrace.WithUUID(r.Context(), requestID) if err != nil { panic(err) } r.SetCtx(newCtx) } r.Middleware.Next() }) userSideServer.BindHandler("/", func(r *ghttp.Request) { ctx := r.Context() g.Log().Info(ctx, "request internalServer starts") content := g.Client().GetContent(ctx, "http://127.0.0.1:8199/") g.Log().Infof(ctx, "internalServer response: %s", content) r.Response.Write(content) }) userSideServer.SetPort(8299) userSideServer.Run() } |
为了演示服务间的链路跟踪能力,这个示例代码中运行了两个HTTP服务,一个内部服务,提供功能逻辑;一个外部服务,供外部的接口调用,它的功能是调用内部服务来实现的。执行后,我们访问:http://127.0.0.1:8299/?RequestID=33612a70-990a-11ea-87fe-fd68517e7a2d
随后查看终端输出:
2022-06-07 14:51:21.957 [INFO] openapi specification is disabled 2022-06-07 14:51:21.958 [INTE] ghttp_server.go:78 78198: graceful reload feature is disabled SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|-------------------- internalServer | default | :8199 | ALL | / | main.main.func1 | -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|-------------------- internalServer | default | :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|-------------------- 2022-06-07 14:51:21.959 [INFO] pid[78198]: http server started listening on [:8199] 2022-06-07 14:51:21.965 [INFO] openapi specification is disabled SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|-------------------- userSideServer | default | :8299 | ALL | / | main.main.func3 | -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|-------------------- userSideServer | default | :8299 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|-------------------- userSideServer | default | :8299 | ALL | /* | main.main.func2 | GLOBAL MIDDLEWARE -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|-------------------- 2022-06-07 14:51:21.965 [INFO] pid[78198]: http server started listening on [:8299] 2022-06-07 14:53:05.322 [INFO] {33612a70990a11ea87fefd68517e7a2d} request internalServer starts 2022-06-07 14:53:05.323 [INFO] {33612a70990a11ea87fefd68517e7a2d} internalServer handler 2022-06-07 14:53:05.323 [INFO] {33612a70990a11ea87fefd68517e7a2d} internalServer response: 33612a70990a11ea87fefd68517e7a2d |
我们发现,RequestID
作为TraceID
贯通了整个服务间的链路了呢!