GoFrame Framework (rk-boot): Implementing Distributed Log Tracking

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"}
...
{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324129745&siteId=291194637