一、基本介绍

二、TraceID的注入

如果使用GoFrame框架的Client,那么所有的请求将会自带TraceID的注入。GoFrameTraceID使用的是OpenTelemetry规范,是由十六进制字符组成的的32字节字符串。

强烈建议大家统一使用gclient组件,不仅功能全面而且自带链路跟踪能力。

2、服务端

如果使用GoFrame框架的Server,如果请求带有TraceID,那么将会自动承接到Context中;否则,将会自动注入标准的TraceID,并传递给后续逻辑。

三、TraceID的获取

1、客户端

如果使用GoFrame框架的Client,这里有三种方式。

通过gctx.New/WithCtx方法创建一个带有TraceIDContext,该Context参数用于传递给请求方法。随后可以通过gctx.CtxId方法获取整个链路的TraceID。相关方法:

这里还有个高级的用法,客户端可以自定义TraceID,使用gtrace.WithTraceID方法。方法定义如下:

  1. // WithTraceID injects custom trace id into context to propagate.
  2. func WithTraceID(ctx context.Context, traceID string) (context.Context, error)

如果是请求 GoFrame 框架的 Server ,那么在返回请求的 Header 中将会增加 Trace-Id 字段,供客户端读取。

如果使用GoFrame框架的Server,直接通过gctx.CtxId方法即可获取TraceID。相关方法:

  1. // CtxId retrieves and returns the context id from context.
  2. func CtxId(ctx context.Context) string

四、使用示例

1、HTTP Response Header TraceID

执行后,终端输出:

  1. ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
  2. ----------|--------|-------|-----------------------------------------------------------------|--------------------
  3. :8199 | ALL | / | main.main.func1 |
  4. ----------|--------|-------|-----------------------------------------------------------------|--------------------
  5. :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE
  6. ----------|--------|-------|-----------------------------------------------------------------|--------------------
  7. 2022-06-06 21:14:37.245 [INFO] pid[55899]: http server started listening on [:8199]
  8. 2022-06-06 21:14:38.247 [INFO] {908d2027560af616e218e912d2ac3972} handler
  9. +---------------------------------------------+
  10. | REQUEST |
  11. +---------------------------------------------+
  12. GET / HTTP/1.1
  13. Host: 127.0.0.1:8199
  14. User-Agent: GClient v2.1.0-rc4 at TXQIANGGUO-MB0
  15. Accept-Encoding: gzip
  16. +---------------------------------------------+
  17. | RESPONSE |
  18. +---------------------------------------------+
  19. HTTP/1.1 200 OK
  20. Connection: close
  21. Content-Length: 32
  22. Content-Type: text/plain; charset=utf-8
  23. Server: GoFrame HTTP Server
  24. Trace-Id: 908d2027560af616e218e912d2ac3972
  25. 908d2027560af616e218e912d2ac3972

2、客户端注入TraceID

  1. package main
  2. import (
  3. "time"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/net/ghttp"
  6. "github.com/gogf/gf/v2/os/gctx"
  7. )
  8. func main() {
  9. s := g.Server()
  10. s.BindHandler("/", func(r *ghttp.Request) {
  11. traceID := gctx.CtxId(r.Context())
  12. g.Log().Info(r.Context(), "handler")
  13. r.Response.Write(traceID)
  14. })
  15. s.SetPort(8199)
  16. go s.Start()
  17. time.Sleep(time.Second)
  18. ctx := gctx.New()
  19. g.Log().Info(ctx, "request starts")
  20. content := g.Client().GetContent(ctx, "http://127.0.0.1:8199/")
  21. g.Log().Infof(ctx, "response: %s", content)
  22. }

执行后,终端输出:

  1. package main
  2. import (
  3. "context"
  4. "time"
  5. "github.com/gogf/gf/v2/frame/g"
  6. "github.com/gogf/gf/v2/net/ghttp"
  7. "github.com/gogf/gf/v2/net/gtrace"
  8. "github.com/gogf/gf/v2/os/gctx"
  9. "github.com/gogf/gf/v2/text/gstr"
  10. )
  11. func main() {
  12. s := g.Server()
  13. s.BindHandler("/", func(r *ghttp.Request) {
  14. g.Log().Info(r.Context(), "handler")
  15. r.Response.Write(traceID)
  16. })
  17. s.SetPort(8199)
  18. time.Sleep(time.Second)
  19. ctx, _ := gtrace.WithTraceID(context.Background(), gstr.Repeat("a", 32))
  20. g.Log().Info(ctx, "request starts")
  21. content := g.Client().GetContent(ctx, "http://127.0.0.1:8199/")
  22. g.Log().Infof(ctx, "response: %s", content)
  23. }
  1. ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
  2. ----------|--------|-------|-----------------------------------------------------------------|--------------------
  3. :8199 | ALL | / | main.main.func1 |
  4. ----------|--------|-------|-----------------------------------------------------------------|--------------------
  5. :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE
  6. ----------|--------|-------|-----------------------------------------------------------------|--------------------
  7. 2022-06-06 21:40:03.897 [INFO] pid[58435]: http server started listening on [:8199]
  8. 2022-06-06 21:40:04.900 [INFO] {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} request starts
  9. 2022-06-06 21:40:04.901 [INFO] {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} handler
  10. 2022-06-06 21:40:04.901 [INFO] {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} response: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

五、常见问题

1、如果没有使用GoFrame框架的Client/Server,如何获取链路的TraceID

可以参考GoFrame框架的Client/Server的链路跟踪实现,并自行实现一遍,这块可能需要一定成本。

如果使用的第三方Client/Server组件,请参考第三方组件相关介绍。

2、企业内部服务没有使用标准的OpenTelemetry规范,但是每个请求都带RequestID参数,形如 33612a70-990a-11ea-87fe-fd68517e7a2d,如何和TraceID结合起来?

根据我的分析,你这个RequestID的格式和TraceID规范非常切合,使用的是UUID字符串,而UUID可直接转换为TraceID。建议在自己的Server内部第一层中间件中将RequestID转换为TraceID,通过自定义TraceID的方式注入到Context中,并将该Context传递给后续业务逻辑。

我来给你写个例子吧:

为了演示服务间的链路跟踪能力,这个示例代码中运行了两个HTTP服务,一个内部服务,提供功能逻辑;一个外部服务,供外部的接口调用,它的功能是调用内部服务来实现的。执行后,我们访问:http://127.0.0.1:8299/?RequestID=33612a70-990a-11ea-87fe-fd68517e7a2d

  1. 2022-06-07 14:51:21.957 [INFO] openapi specification is disabled
  2. 2022-06-07 14:51:21.958 [INTE] ghttp_server.go:78 78198: graceful reload feature is disabled
  3. SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
  4. -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
  5. internalServer | default | :8199 | ALL | / | main.main.func1 |
  6. -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
  7. internalServer | default | :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE
  8. -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
  9. 2022-06-07 14:51:21.959 [INFO] pid[78198]: http server started listening on [:8199]
  10. 2022-06-07 14:51:21.965 [INFO] openapi specification is disabled
  11. SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
  12. -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
  13. userSideServer | default | :8299 | ALL | / | main.main.func3 |
  14. -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
  15. userSideServer | default | :8299 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE
  16. -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
  17. userSideServer | default | :8299 | ALL | /* | main.main.func2 | GLOBAL MIDDLEWARE
  18. -----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
  19. 2022-06-07 14:51:21.965 [INFO] pid[78198]: http server started listening on [:8299]
  20. 2022-06-07 14:53:05.322 [INFO] {33612a70990a11ea87fefd68517e7a2d} request internalServer starts
  21. 2022-06-07 14:53:05.323 [INFO] {33612a70990a11ea87fefd68517e7a2d} internalServer response: 33612a70990a11ea87fefd68517e7a2d

我们发现,RequestID作为TraceID贯通了整个服务间的链路了呢!