introduce
Through a complete example, distributed log tracking is implemented based on the gogf/gf framework.
What is API Log Tracing?
An API request will span multiple microservices, and we want to retrieve the logs of the entire link through a unique ID.
We will use rk-boot to start the gogf/gf microservice .
Please visit the following address for the complete tutorial:
Install
go get github.com/rookie-ninja/rk-boot/gf
quick start
We will create /v1/greeter API for verification, and enable logging, meta and tracing middleware to achieve the purpose.
1. Bhoot bootA.yaml & serverA.go
ServerA listens on port 1949 and sends requests to ServerB.
We inject the Tracing information into the Context through the rkgfctx.InjectSpanToNewContext() method and send it to ServerB.
---
gf:
- name: greeter # Required
port: 1949 # Required
enabled: true # Required
interceptors:
loggingZap:
enabled: true # Optional, enable logging interceptor
meta:
enabled: true # Optional, enable meta interceptor
tracingTelemetry:
enabled: true # Optional, enable tracing interceptor
// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/rookie-ninja/rk-boot"
"github.com/rookie-ninja/rk-boot/gf"
"github.com/rookie-ninja/rk-gf/interceptor/context"
"net/http"
)
// Application entrance.
func main() {
// Create a new boot instance.
boot := rkboot.NewBoot(rkboot.WithBootConfigPath("bootA.yaml"))
// Register handler
entry := rkbootgf.GetGfEntry("greeter")
entry.Server.BindHandler("/v1/greeter", GreeterA)
// Bootstrap
boot.Bootstrap(context.TODO())
boot.WaitForShutdownSig(context.TODO())
}
// GreeterA will add trace info into context and call serverB
func GreeterA(ctx *ghttp.Request) {
// Call serverB at 2008
req, _ := http.NewRequest(http.MethodGet, "http://localhost:2008/v1/greeter", nil)
// Inject current trace information into context
rkgfctx.InjectSpanToHttpRequest(ctx, req)
// Call server
http.DefaultClient.Do(req)
ctx.Response.WriteHeader(http.StatusOK)
ctx.Response.Write("Hello from serverA!")
}
2. Create bootB.yaml & serverB.go
ServerB listens on port 2008.
---
gf:
- name: greeter # Required
port: 2008 # Required
enabled: true # Required
interceptors:
loggingZap:
enabled: true # Optional, enable logging interceptor
meta:
enabled: true # Optional, enable meta interceptor
tracingTelemetry:
enabled: true # Optional, enable tracing interceptor
// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/rookie-ninja/rk-boot"
"github.com/rookie-ninja/rk-boot/gf"
"net/http"
)
// Application entrance.
func main() {
// Create a new boot instance.
boot := rkboot.NewBoot(rkboot.WithBootConfigPath("bootB.yaml"))
// Register handler
entry := rkbootgf.GetGfEntry("greeter")
entry.Server.BindHandler("/v1/greeter", GreeterB)
// Bootstrap
boot.Bootstrap(context.TODO())
boot.WaitForShutdownSig(context.TODO())
}
// GreeterB will add trace info into context and call serverB
func GreeterB(ctx *ghttp.Request) {
ctx.Response.WriteHeader(http.StatusOK)
ctx.Response.Write("Hello from serverB!")
}
3. Folder structure
.
├── bootA.yaml
├── bootB.yaml
├── go.mod
├── go.sum
├── serverA.go
└── serverB.go
0 directories, 6 files
4. Start ServerA & ServerB
$ go run serverA.go
$ go run serverB.go
5. Send a request to ServerA
$ curl localhost:1949/v1/greeter
Hello from serverA!
6. Verify the log
In the logs of the two services, there will be the same traceId and different requestId.
We can trace the RPC by grep traceId.
- ServerA
------------------------------------------------------------------------
endTime=2022-01-22T02:11:19.946305+08:00
...
ids={"eventId":"4092f609-bc5d-4b61-abef-7910f417fc36","requestId":"4092f609-bc5d-4b61-abef-7910f417fc36","traceId":"bb272da5fbf68182037bd7a3bdcb8a8b"}
...
operation=/v1/greeter
resCode=200
eventStatus=Ended
EOE
- ServerB
------------------------------------------------------------------------
endTime=2022-01-22T02:11:19.946091+08:00
...
ids={"eventId":"dbb4b360-86f8-4dfe-aa4c-ef8dae5686d8","requestId":"dbb4b360-86f8-4dfe-aa4c-ef8dae5686d8","traceId":"bb272da5fbf68182037bd7a3bdcb8a8b"}
...
operation=/v1/greeter
resCode=200
eventStatus=Ended
EOE
concept
We want to track RPC requests in a distributed system through logs when we are not using chain services such as jaeger.
The middleware of rk-boot will use the openTelemetry library to write the traceId to the log to trace the RPC.
When the log middleware, the original data middleware, and the call chain middleware are started, the middleware will write the following three IDs into the log.
EventId
When the logging middleware is started, the EventId is automatically generated.
---
gf:
- name: greeter # Required
port: 1949 # Required
enabled: true # Required
interceptors:
loggingZap:
enabled: true
------------------------------------------------------------------------
...
ids={"eventId":"cd617f0c-2d93-45e1-bef0-95c89972530d"}
...
RequestId
When the log middleware and the original data middleware are started, RequestId and EventId will be automatically generated, and these two IDs will be consistent.
---
gf:
- name: greeter # Required
port: 1949 # Required
enabled: true # Required
interceptors:
loggingZap:
enabled: true
meta:
enabled: true
------------------------------------------------------------------------
...
ids={"eventId":"8226ba9b-424e-4e19-ba63-d37ca69028b3","requestId":"8226ba9b-424e-4e19-ba63-d37ca69028b3"}
...
Even if the user overrides the RequestId, the EventId will remain the same.
rkgfctx.AddHeaderToClient(ctx, rkmid.HeaderRequestId, "overridden-request-id")
------------------------------------------------------------------------
...
ids={"eventId":"overridden-request-id","requestId":"overridden-request-id"}
...
TraceId
When the call chain middleware is started, the traceId is automatically generated.
---
gf:
- name: greeter # Required
port: 1949 # Required
enabled: true # Required
interceptors:
loggingZap:
enabled: true
meta:
enabled: true
tracingTelemetry:
enabled: true
------------------------------------------------------------------------
...
ids={"eventId":"dd19cf9a-c7be-486c-b29d-7af777a78ebe","requestId":"dd19cf9a-c7be-486c-b29d-7af777a78ebe","traceId":"316a7b475ff500a76bfcd6147036951c"}
...