1. Introduction

In tracing, TraceID serves as a unique identifier passed between various services, used to associate requests between services, making it a crucial piece of data. TraceID is passed through the Context parameter, and if you use the framework’s glog logging component, the TraceID will automatically be read and recorded in the log content. Therefore, it is recommended to use the framework’s glog logging component for logging to perfectly support the tracing feature.

2. Injection of TraceID

1. Client

If using the GoFrame framework’s Client, all requests will inherently include TraceID injection. GoFrame utilizes the OpenTelemetry standard for its TraceID, which is a 32 hexadecimal character string.

TraceID Injection and Retrieval - 图1tip

It is strongly recommended to use the gclient component, which is both feature-rich and equipped with tracing capabilities.

2. Server

If using the GoFrame framework’s Server, if the request includes a TraceID, it will be automatically inherited into the Context; otherwise, a standard TraceID will be automatically injected and passed to subsequent logic.

3. Retrieval of TraceID

1. Client

If using the GoFrame framework’s Client, there are three methods available here.

Create a Context with a TraceID using the gctx.New/WithCtx method, and pass this Context parameter when making requests. You can subsequently retrieve the TraceID for the entire trace using the gctx.CtxId method. Relevant methods:

  1. // New creates and returns a context which contains context id.
  2. func New() context.Context
  3. // WithCtx creates and returns a context containing context id upon given parent context `ctx`.
  4. func WithCtx(ctx context.Context) context.Context

When using the WithCtx method, if the given ctx parameter already contains a TraceID, it will be used directly without creating a new one.

2) Client Custom TraceID

An advanced usage is that the client can customize the TraceID using the gtrace.WithTraceID method. The method is defined as follows:

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

3) Read Response Header

If it is a request to a GoFrame framework Server, the Trace-Id field will be added to the returned request’s Header for the client to read.

2. Server

If using the GoFrame framework’s Server, you can directly obtain the TraceID using the gctx.CtxId method. Relevant methods:

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

4. Examples of Use

1. HTTP Response Header TraceID

  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/os/gctx"
  8. )
  9. func main() {
  10. s := g.Server()
  11. s.BindHandler("/", func(r *ghttp.Request) {
  12. traceID := gctx.CtxId(r.Context())
  13. g.Log().Info(r.Context(), "handler")
  14. r.Response.Write(traceID)
  15. })
  16. s.SetPort(8199)
  17. go s.Start()
  18. time.Sleep(time.Second)
  19. req, err := g.Client().Get(context.Background(), "http://127.0.0.1:8199/")
  20. if err != nil {
  21. panic(err)
  22. }
  23. defer req.Close()
  24. req.RawDump()
  25. }

After execution, the terminal output:

  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. Traceparent: 00-908d2027560af616e218e912d2ac3972-1e291041b9afa718-01
  16. Accept-Encoding: gzip
  17. +---------------------------------------------+
  18. | RESPONSE |
  19. +---------------------------------------------+
  20. HTTP/1.1 200 OK
  21. Connection: close
  22. Content-Length: 32
  23. Content-Type: text/plain; charset=utf-8
  24. Date: Mon, 06 Jun 2022 13:14:38 GMT
  25. Server: GoFrame HTTP Server
  26. Trace-Id: 908d2027560af616e218e912d2ac3972
  27. 908d2027560af616e218e912d2ac3972

2. Injecting TraceID in Client

  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. }

After execution, the terminal output:

  1. 2022-06-06 21:17:17.447 [INFO] pid[56070]: http server started listening on [:8199]
  2. ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
  3. ----------|--------|-------|-----------------------------------------------------------------|--------------------
  4. :8199 | ALL | / | main.main.func1 |
  5. ----------|--------|-------|-----------------------------------------------------------------|--------------------
  6. :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE
  7. ----------|--------|-------|-----------------------------------------------------------------|--------------------
  8. 2022-06-06 21:17:18.450 [INFO] {e843f0737b0af616d8ed185d46ba65c5} request starts
  9. 2022-06-06 21:17:18.451 [INFO] {e843f0737b0af616d8ed185d46ba65c5} handler
  10. 2022-06-06 21:17:18.451 [INFO] {e843f0737b0af616d8ed185d46ba65c5} response: e843f0737b0af616d8ed185d46ba65c5

3. Custom TraceID in Client

  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. traceID := gctx.CtxId(r.Context())
  15. g.Log().Info(r.Context(), "handler")
  16. r.Response.Write(traceID)
  17. })
  18. s.SetPort(8199)
  19. go s.Start()
  20. time.Sleep(time.Second)
  21. ctx, _ := gtrace.WithTraceID(context.Background(), gstr.Repeat("a", 32))
  22. g.Log().Info(ctx, "request starts")
  23. content := g.Client().GetContent(ctx, "http://127.0.0.1:8199/")
  24. g.Log().Infof(ctx, "response: %s", content)
  25. }

After execution, the terminal output:

  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

5. Common Questions

1. If not using GoFrame framework’s Client/Server, how to obtain the trace’s TraceID?

Refer to the implementation of trace in GoFrame framework’s Client/Server and implement it manually. This may involve certain costs.

If using a third-party Client/Server component, refer to the relevant documentation of the third-party component.

2. Internal services do not use the standard OpenTelemetry protocol, but each request carries a RequestID parameter in the form of 33612a70-990a-11ea-87fe-fd68517e7a2d. How to integrate with TraceID?

Based on my analysis, your RequestID format closely aligns with the TraceID standard, utilizing a UUID string, which can be directly converted to a TraceID. It is suggested to convert RequestID to TraceID in the first middleware layer within your Server internally and inject it into the Context using the custom TraceID method, passing the Context to subsequent business logic.

Here’s an example:

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/frame/g"
  4. "github.com/gogf/gf/v2/net/ghttp"
  5. "github.com/gogf/gf/v2/net/gtrace"
  6. "github.com/gogf/gf/v2/os/gctx"
  7. )
  8. func main() {
  9. // Internal service
  10. internalServer := g.Server("internalServer")
  11. internalServer.BindHandler("/", func(r *ghttp.Request) {
  12. traceID := gctx.CtxId(r.Context())
  13. g.Log().Info(r.Context(), "internalServer handler")
  14. r.Response.Write(traceID)
  15. })
  16. internalServer.SetPort(8199)
  17. go internalServer.Start()
  18. // External service, accessed for testing
  19. // http://127.0.0.1:8299/?RequestID=33612a70-990a-11ea-87fe-fd68517e7a2d
  20. userSideServer := g.Server("userSideServer")
  21. userSideServer.Use(func(r *ghttp.Request) {
  22. requestID := r.Get("RequestID").String()
  23. if requestID != "" {
  24. newCtx, err := gtrace.WithUUID(r.Context(), requestID)
  25. if err != nil {
  26. panic(err)
  27. }
  28. r.SetCtx(newCtx)
  29. }
  30. r.Middleware.Next()
  31. })
  32. userSideServer.BindHandler("/", func(r *ghttp.Request) {
  33. ctx := r.Context()
  34. g.Log().Info(ctx, "request internalServer starts")
  35. content := g.Client().GetContent(ctx, "http://127.0.0.1:8199/")
  36. g.Log().Infof(ctx, "internalServer response: %s", content)
  37. r.Response.Write(content)
  38. })
  39. userSideServer.SetPort(8299)
  40. userSideServer.Run()
  41. }

This example code runs two HTTP services to demonstrate inter-service tracing capabilities. One is an internal service that provides functionality; the other is an external service for testing, which achieves its functionality by calling the internal service. After execution, access: http://127.0.0.1:8299/?RequestID=33612a70-990a-11ea-87fe-fd68517e7a2d

Then check the terminal output:

  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 handler
  22. 2022-06-07 14:53:05.323 [INFO] {33612a70990a11ea87fefd68517e7a2d} internalServer response: 33612a70990a11ea87fefd68517e7a2d

We can see that the RequestID has been successfully circulated as the TraceID through the service chain!