Introduction: This article describes how to implement API log tracing in a gRPC distributed scenario.

introduce

This article will show you how to implement API log tracking in a gRPC distributed scenario.

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 gRPC service.

Please visit the following address for the full tutorial:

Rkdev. Info/CNHTTPS: / / r… (standby)

The installation

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

Quick start

Rk-boot integrates grPC-gateway by default and starts by default.

We create/API /v1/ Greeter apis for validation and enable logging, Meta, and tracing interceptors.

1. Create API/v1 / greeter. Proto

syntax = "proto3";

package api.v1;

option go_package = "api/v1/greeter";

service Greeter {
  rpc Greeter (GreeterRequest) returns (GreeterResponse) {}
}

message GreeterRequest {
  string name = 1;
}

message GreeterResponse {
  string message = 1;
}
Copy the code

2. Create the API/v1 / gw_mapping. Yaml

type: google.api.Service
config_version: 3

# Please refer google.api.Http in https://github.com/googleapis/googleapis/blob/master/google/api/http.proto file for details.
http:
  rules:
    - selector: api.v1.Greeter.Greeter
      get: /api/v1/greeter
Copy the code

3. Create buf. Yaml

version: v1beta1
name: github.com/rk-dev/rk-demo
build:
  roots:
    - api
Copy the code

4. Create buf. Gen. Yaml

version: v1beta1
plugins:
  # protoc-gen-go needs to be installed, generate go files based on proto files
  - name: go
    out: api/gen
    opt:
     - paths=source_relative
  # protoc-gen-go-grpc needs to be installed, generate grpc go files based on proto files
  - name: go-grpc
    out: api/gen
    opt:
      - paths=source_relative
      - require_unimplemented_servers=false
  # protoc-gen-grpc-gateway needs to be installed, generate grpc-gateway go files based on proto files
  - name: grpc-gateway
    out: api/gen
    opt:
      - paths=source_relative
      - grpc_api_configuration=api/v1/gw_mapping.yaml
  # protoc-gen-openapiv2 needs to be installed, generate swagger config files based on proto files
  - name: openapiv2
    out: api/gen
    opt:
      - grpc_api_configuration=api/v1/gw_mapping.yaml
Copy the code

5. Compile proto file

$ buf generate
Copy the code

The following files will be created.

$tree API/API/gen gen └ ─ ─ v1 ├ ─ ─ greeter. Pb. Go ├ ─ ─ greeter. Pb. Gw go ├ ─ ─ greeter. Swagger. Json └ ─ ─ greeter_grpc. Pb. Go 1 directory, 4 filesCopy the code

6. Create boota.yaml & Servera.go

Server-a listens on port 1949 and sends A request to server-B.

We through RKGRPCCTX InjectSpanToNewContext () method into Context, the Tracing information sent to the Server – B.

--- grpc: - name: greeter # Name of grpc entry port: 1949 # Port of grpc entry enabled: true # Enable grpc entry interceptors: loggingZap: enabled: true meta: enabled: true tracingTelemetry: enabled: true package main import ( "context" "demo/api/gen/v1" "fmt" "github.com/rookie-ninja/rk-boot" "github.com/rookie-ninja/rk-grpc/interceptor/context" "google.golang.org/grpc" ) // Application entrance. func main() { // Create a new boot instance. boot := rkboot.NewBoot(rkboot.WithBootConfigPath("bootA.yaml")) // Get grpc entry with name grpcEntry := boot.GetGrpcEntry("greeter") grpcEntry.AddRegFuncGrpc(registerGreeter) grpcEntry.AddRegFuncGw(greeter.RegisterGreeterHandlerFromEndpoint) // Bootstrap boot.Bootstrap(context.Background()) // Wait for shutdown sig boot.WaitForShutdownSig(context.Background()) } func registerGreeter(server *grpc.Server) { greeter.RegisterGreeterServer(server, &GreeterServer{}) } type GreeterServer struct{} func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) { // Call serverB at 2008 with grpc client opts := []grpc.DialOption{ grpc.WithBlock(), grpc.WithInsecure(), } conn, _ := grpc.Dial("localhost:2008", opts...) defer conn.Close() client := greeter.NewGreeterClient(conn) // Inject current trace information into context newCtx := rkgrpcctx.InjectSpanToNewContext(ctx) client.Greeter(newCtx, &greeter.GreeterRequest{Name: "A"}) return &greeter.GreeterResponse{ Message: fmt.Sprintf("Hello %s!" , request.Name), }, nil }Copy the code

7. Create bootb.yaml & serverB. Go

Server-b listens on port 2008.

--- grpc: - name: greeter # Name of grpc entry port: 2008 # Port of grpc entry enabled: true # Enable grpc entry interceptors: loggingZap: enabled: true meta: enabled: true tracingTelemetry: enabled: true package main import ( "context" "demo/api/gen/v1" "fmt" "github.com/rookie-ninja/rk-boot" "google.golang.org/grpc" ) // Application entrance. func main() { // Create a new boot instance. boot := rkboot.NewBoot(rkboot.WithBootConfigPath("bootB.yaml")) // Get grpc entry with name grpcEntry := boot.GetGrpcEntry("greeter") grpcEntry.AddRegFuncGrpc(registerGreeterB) grpcEntry.AddRegFuncGw(greeter.RegisterGreeterHandlerFromEndpoint) // Bootstrap boot.Bootstrap(context.Background()) // Wait for shutdown sig boot.WaitForShutdownSig(context.Background()) } func registerGreeterB(server *grpc.Server) { greeter.RegisterGreeterServer(server, &GreeterServerB{}) } type GreeterServerB struct{} func (server *GreeterServerB) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) { return &greeter.GreeterResponse{ Message: fmt.Sprintf("Hello %s!" , request.Name), }, nil }Copy the code

8. Folder structure

├ ─ ─ API │ ├ ─ ─ gen │ │ └ ─ ─ v1 │ │ ├ ─ ─ greeter. Pb. Go │ │ ├ ─ ─ greeter. Pb. Gw go │ │ ├ ─ ─ greeter. Swagger. Json │ │ └ ─ ─ Greeter_grpc. Pb. Go │ └ ─ ─ v1 │ ├ ─ ─ greeter. Proto │ └ ─ ─ gw_mapping. Yaml ├ ─ ─ bootA. Yaml ├ ─ ─ bootB. Yaml ├ ─ ─ buf. Gen. Yaml ├ ─ ─ ├── bass exercises ── bass Exercises ── bass ExercisesCopy the code

9. Start ServerA & ServerB

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

10. Send a request to ServerA

Selections curl "localhost: 1949 / API/v1 / greeter? name=rk-dev"Copy the code

11. 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-10-20 T00:02:21. 739688 + 08:00… ids={“eventId”:”0d145356-998a-4999-ab62-6f1b805274a0″,”requestId”:”0d145356-998a-4999-ab62-6f1b805274a0″,”traceId”:”c36a 45eb076066df39fa407174012369″} … operation=/api.v1.Greeter/Greeter resCode=OK eventStatus=Ended EOE

  • ServerB


    EndTime = 2021-10-20 T00:02:21. 739125 + 08:00… ids={“eventId”:”8858a6eb-e953-42ad-bdc3-c466bbbd798e”,”requestId”:”8858a6eb-e953-42ad-bdc3-c466bbbd798e”,”traceId”:”c36a 45eb076066df39fa407174012369″} … operation=/api.v1.Greeter/Greeter resCode=OK eventStatus=Ended EOE

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 RK-Boot interceptor writes traceId to the log via the openTelemetry library to trace the RPC.

When a log interceptor, raw data interceptor, or chain interceptor is started, the interceptor writes the following three ids to the log.

EventId

EventId is generated automatically when the log interceptor is started.

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 1949                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    interceptors:
      loggingZap:
        enabled: true

------------------------------------------------------------------------
...
ids={"eventId":"cd617f0c-2d93-45e1-bef0-95c89972530d"}
...
Copy the code

RequestId

When the log interceptor and raw data interceptor are started, the RequestId and EventId are automatically generated and the two ids are consistent.

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 1949                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    interceptors:
      loggingZap:
        enabled: true
      meta:
        enabled: true

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

rkgrpcctx.AddHeaderToClient(ctx, rkgrpcctx.RequestIdKey, "overridden-request-id")

------------------------------------------------------------------------
...
ids={"eventId":"overridden-request-id","requestId":"overridden-request-id"}
...
Copy the code

TraceId

TraceId is generated automatically when the call chain interceptor is started.

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

The original link

This article is the original content of Aliyun and shall not be reproduced without permission.