This is the 11th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021

introduce

With a complete example, distributed log tracking is implemented in a Gin based framework.

What is API log tracing?

An API request spans multiple microservices, and we want to retrieve logs for the entire link with a unique ID.

We will use RK-boot to start the Gin framework microservice.

Please visit the following address for the full tutorial:

  • rkdocs.netlify.app/cn

The installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-gin
Copy the code

Quick start

We created /v1/ Greeter apis for validation, and enabled logging, Meta, and tracing middleware to achieve this.

1. Create boota. yaml & Servera. go

ServerA listens on port 1949 and sends a request to ServerB.

We through rkginctx InjectSpanToNewContext () method of the Tracing information into Context, sent to ServerB.

---
gin:
  - 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
Copy the code
// 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/gin-gonic/gin"
	"github.com/rookie-ninja/rk-boot"
        "github.com/rookie-ninja/rk-gin/boot"
	"github.com/rookie-ninja/rk-gin/interceptor/context"
	"net/http"
)

// Application entrance.
func main(a) {
	// Create a new boot instance.
	boot := rkboot.NewBoot(rkboot.WithBootConfigPath("bootA.yaml"))

	// Register handler
	boot.GetEntry("greeter").(*rkgin.GinEntry).Router.GET("/v1/greeter", GreeterA)

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

// GreeterA will add trace info into context and call serverB
func GreeterA(ctx *gin.Context) {
	// Call serverB at 2008
	req, _ := http.NewRequest(http.MethodGet, "http://localhost:2008/v1/greeter".nil)

	// Inject current trace information into context
	rkginctx.InjectSpanToHttpRequest(ctx, req)

	// Call server
	http.DefaultClient.Do(req)
	
	// Respond to request
	ctx.String(http.StatusOK, "Hello from serverA!")}Copy the code

2. Create bootb.yaml & SerPreb.go

ServerB listens on port 2008.

---
gin:
  - 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
Copy the code
// 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/gin-gonic/gin"
	"github.com/rookie-ninja/rk-boot"
        "github.com/rookie-ninja/rk-gin/boot"
	"net/http"
)

// Application entrance.
func main(a) {
	// Create a new boot instance.
	boot := rkboot.NewBoot(rkboot.WithBootConfigPath("bootB.yaml"))

	// Register handler
	boot.GetEntry("greeter").(*rkgin.GinEntry).Router.GET("/v1/greeter", GreeterB)

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

// GreeterB receive request from serverA and respond.
func GreeterB(ctx *gin.Context) {
	ctx.String(http.StatusOK, "Hello from serverB!")}Copy the code

3. Folder structure

.├ ── ─ Go.go ├── Go.go ├── Go.go ├── go.go ├── let directories directories directories, 6 filesCopy the code

4. Start ServerA & ServerB

$ go run serverA.go
$ go run serverB.go
Copy the code

5. Send a request to ServerA

$ curl localhost:1949/v1/greeter
Hello from serverA!
Copy the code

6. Verify logs

The logs of the two services have the same traceId but different requestId.

We can trace RPC by grep traceId.

  • ServerA
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- endTime = 2021-11-18 T01:29:56. 698997 + 08:00... ids={"eventId":"f5878390-1a5a-4bb9-8b39-bf4261864c0f","requestId":"f5878390-1a5a-4bb9-8b39-bf4261864c0f","traceId":"b2d7 0ab9f8207ef4a9f0c3fb1be5c22c"} ... operation=/v1/greeter resCode=200 eventStatus=Ended EOECopy the code
  • ServerB
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- endTime = 2021-11-18 T01:29:56. 698606 + 08:00... ids={"eventId":"273c97d2-e11a-46f5-a044-bb9c0cf64540","requestId":"273c97d2-e11a-46f5-a044-bb9c0cf64540","traceId":"b2d7 0ab9f8207ef4a9f0c3fb1be5c22c"} ... operation=/v1/greeter resCode=200 eventStatus=Ended EOECopy the code

concept

When we are not calling chain services such as Jaeger, we want to keep track of RPC requests in a distributed system through logging.

The MIDDLEWARE of RK-Boot writes traceId to the log through the openTelemetry library to trace the RPC.

When the logging middleware, raw data middleware and call chain middleware are started, the middleware will write the following three ids in the log.

EventId

When the logging middleware is started, EventId is automatically generated.

---
gin:
  - name: greeter                   # Required
    port: 1949                      # Required
    enabled: true                   # Required
    interceptors:
      loggingZap:
        enabled: true
Copy the code
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --... ids={"eventId":"cd617f0c-2d93-45e1-bef0-95c89972530d"} ...Copy the code

RequestId

When the logging middleware and raw data middleware are enabled, the RequestId and EventId ids are automatically generated and the two ids are consistent.

---
gin:
  - name: greeter                   # Required
    port: 1949                      # Required
    enabled: true                   # Required
    interceptors:
      loggingZap:
        enabled: true
      meta:
        enabled: true
Copy the code
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --... ids={"eventId":"8226ba9b-424e-4e19-ba63-d37ca69028b3","requestId":"8226ba9b-424e-4e19-ba63-d37ca69028b3"} ...Copy the code

EventId remains consistent even if the user overrides RequestId.

rkginctx.AddHeaderToClient(ctx, rkginctx.RequestIdKey, "overridden-request-id")
Copy the code
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --... ids={"eventId":"overridden-request-id","requestId":"overridden-request-id"} ...Copy the code

TraceId

When the call chain middleware is enabled, traceId is automatically generated.

---
gin:
  - name: greeter                   # Required
    port: 1949                      # Required
    enabled: true                   # Required
    interceptors:
      loggingZap:
        enabled: true
      meta:
        enabled: true
      tracingTelemetry:
        enabled: true
Copy the code
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --... ids={"eventId":"dd19cf9a-c7be-486c-b29d-7af777a78ebe","requestId":"dd19cf9a-c7be-486c-b29d-7af777a78ebe","traceId":"316a 7b475ff500a76bfcd6147036951c"} ...Copy the code