GoLang链路追踪trace

req, err := http.NewRequest("GET", fmt.Sprintf("http://127.0.0.1:777/ping?x-logid=%s", logFormater.LogId), nil)
util.Must(err)

ctx := self.C.Request.Context()
span := opentracing.SpanFromContext(ctx)
if span == nil {
	panic("nil span")
}

//trace info 注入req.Header
span.Tracer().Inject(span.Context(),opentracing.HTTPHeaders,opentracing.HTTPHeadersCarrier(req.Header))

req.Header.Set("x-hop", logFormater.XHop.Hex())

resp, err := http.DefaultClient.Do(req)
util.Must(err)

bs, err := ioutil.ReadAll(resp.Body)
util.Must(err)
fmt.Println(bs)

 

package trace

import (
	"net/http"
	"net/url"
	"time"
	"why/log"

	"github.com/gin-gonic/gin"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
)

const defaultComponentName = "net/http"

type Options struct {
	tracer        opentracing.Tracer
	opNameFunc    func(r *http.Request) string
	spanObserver  func(span opentracing.Span, r *http.Request)
	urlTagFunc    func(u *url.URL) string
	componentName string
}

type OptionsFunc func(*Options)

func OperationNameFunc(f func(r *http.Request) string) OptionsFunc {
	return func(options *Options) {
		options.opNameFunc = f
	}
}

func WithTracer(tracer opentracing.Tracer) OptionsFunc {
	return func(options *Options) {
		options.tracer = tracer
	}
}

func WithComponentName(componentName string) OptionsFunc {
	return func(options *Options) {
		options.componentName = componentName
	}
}

func WithSpanObserver(f func(span opentracing.Span, r *http.Request)) OptionsFunc {
	return func(options *Options) {
		options.spanObserver = f
	}
}

func WithURLTagFunc(f func(u *url.URL) string) OptionsFunc {
	return func(options *Options) {
		options.urlTagFunc = f
	}
}

//OpenTracing 链路追踪中间件
//实现了[opentracing](https://opentracing.io)协议
//tracer默认使用jaeger.Tracer,如需修改,可用中间件WithTracer
func OpenTracing(serviceName string, options ...OptionsFunc) gin.HandlerFunc {
	opts := Options{
		opNameFunc: func(r *http.Request) string {
			return r.Proto + " " + r.Method
		},
		spanObserver: func(span opentracing.Span, r *http.Request) {},
		urlTagFunc: func(u *url.URL) string {
			return u.String()
		},
	}
	for _, opt := range options {
		opt(&opts)
	}

	if opts.tracer == nil {
		opts.tracer = opentracing.GlobalTracer()
	}

	return func(c *gin.Context) {
		carrier := opentracing.HTTPHeadersCarrier(c.Request.Header)
		spanContext, _ := opts.tracer.Extract(opentracing.HTTPHeaders, carrier)
		op := opts.opNameFunc(c.Request)
		sp := opts.tracer.StartSpan(op, opentracing.ChildOf(spanContext), opentracing.StartTime(time.Now()))
		ext.HTTPMethod.Set(sp, c.Request.Method)
		ext.HTTPUrl.Set(sp, opts.urlTagFunc(c.Request.URL))
		opts.spanObserver(sp, c.Request)

		componentName := opts.componentName
		if componentName == "" {
			componentName = defaultComponentName
		}

		ext.Component.Set(sp, componentName)
		c.Request = c.Request.WithContext(
			opentracing.ContextWithSpan(c.Request.Context(), sp))
		//trace info 注入resp.Header
		sp.Tracer().Inject(sp.Context(), opentracing.HTTPHeaders,
			opentracing.HTTPHeadersCarrier(c.Writer.Header()))
		c.Next()
		ext.HTTPStatusCode.Set(sp, uint16(c.Writer.Status()))
		sp.FinishWithOptions(opentracing.FinishOptions{FinishTime: time.Now()})
	}
}

type TraceInjecter struct {}
//rpc调用时,trace注入header
func (injecter *TraceInjecter)InjectTrace(c *gin.Context, logFormater *log.LogFormat, req *http.Request) {
	ctx := c.Request.Context()
	span := opentracing.SpanFromContext(ctx)
	if span == nil {
		panic("nil span")
	}

	//trace info 注入req.Header
	span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders,
		opentracing.HTTPHeadersCarrier(req.Header))

	req.Header.Set("x-hop", logFormater.XHop.Hex())
}

 

 

package xhop

import (
	"bytes"
	"encoding/hex"
	"errors"
	"fmt"
	"strconv"
)

//
// Generally speaking,for every http request, the hierarchy of the http rpc calls is a DAG(directed acycline graph).
//
// The X-Hop system is designed to accurately track the hierarchical http rpc call details. It use Hop, a binary encoded integer, to describe every module's hierarchy in the entire DAG from beginning/app to the farest backend end.
//
// In X-Hop system, a module is a http server instance that serves http requests and possibly send one or more rpc calls to other modules. A module must send correct X-Hop value in the request header X-Hop field when sending a http call and its downstream then receives the X-Hop from the request header. When a module receive a request without a X-Hop, it should set X-Hop to 1. The Hop value of a module is calcuted as follows:
//
// 1. Every positive bit in the X-Hop indicates a module.
// 2. A module's X-Hop stripping the leftmost positive bit indicates its parent's X-Hop.
//   a. The root module's (usually the outer GFW) Hop is always  1.
//   b. The count of positive bit in a module's X-Hop indicates the hierarchy of the entire http rpc calls for the specific module.
// 2. The ith http call's X-hop sending from a module M should add (100...0)b to the leftmost of M's X-Hop. There as (i - 1) 0s in total.
//
// For example, module A calls B and C, and B calls D and E, and C calls F, and D calls G at last.
// The DAG shows as follows:
//
//		  	               A
//		  	              / \
//		  	             B   C
//		  	            / \  |
//		  	           D   E F
//		  	          / \    |
//		  	         G   G2  H
//		  	        / \     /|\
//		  	       I   I2  J K L
//			      / \        |
//		         M   M2      N
//		        / \          |
//		       O   P         Q
//	              / \        |
//	             R   S       T
//	                / \      |
//	               U   V     W
//                     |    / \
//                     X   Y   Z
//
// Suppose Hop(i) indicates the module i's Hop, then:
// Hop(A) = (00000001)b 1
// Hop(B) = (00000011)b 3
// Hop(C) = (00000101)b 5
// Hop(D) = (00000111)b 7
// Hop(E) = (00001011)b 11
// Hop(F) = (00001101)b 13
// Hop(G) = (00001111)b
// Hop(G2) = (00010111)b
// Hop(H) = (00011101)b
// Hop(I) = (00011111)b
// Hop(I2) = (00101111)b
// Hop(J) = (00111101)b
// Hop(K) = (01011101)b
// Hop(L) = (10011101)b
// Hop(M) = (00111111)b
// Hop(M2) = (01011111)b
// Hop(N) = (11011101)b
// Hop(O) = (01111111)b
// Hop(P) = (10111111)b
// Hop(Q) = (00000001 11011101)b
// Hop(R) = (00000001 10111111)b
// Hop(S) = (00000010 10111111)b
// Hop(T) = (00000011 11011101)b
// Hop(U) = (00000110 10111111)b
// Hop(V) = (00001010 10111111)b
// Hop(W) = (00000111 11011101)b
// Hop(X) = (00011010 10111111)b
// Hop(Y) = (00001111 11011101)b

type XHop struct {
	buf []byte //use little endian for efficiency
	c   uint64 //current children rpc call counter
}

func NewXhopNull() *XHop {
	b := &XHop{
		buf: make([]byte, 1),
	}
	b.buf[0] = byte(0)

	return b
}

func NewXHop() *XHop {
	b := &XHop{
		buf: make([]byte, 1),
	}
	b.buf[0] = byte(1)

	return b
}

func (x *XHop) MarshalJSON() ([]byte, error) {
	if x == nil {
		return nil, errors.New("nil XHop")
	}

	return []byte(fmt.Sprintf("\"%s\"", x.Hex())), nil
}

func (x *XHop) IsRootXHop() bool {
	if x == nil {
		return false
	}

	return len(x.buf) == 1 && x.buf[0] == byte(1)
}

func NewFromHex(s string) (*XHop, error) {
	if buf, err := hex.DecodeString(s); err != nil {
		return nil, err
	} else {
		//change buf from BigEndian to LittleEndian
		//swap
		var (
			i int = 0
			j int = len(buf) - 1
			p byte
		)

		for i < j {
			p = buf[i]
			buf[i] = buf[j]
			buf[j] = p
			i++
			j--
		}
		//the byteorder of buf is already LittleEndian now.
		//the last byte, the most important byte, should not be zero.
		if buf[len(buf)-1] == 0 {
			return nil, fmt.Errorf("corrupted xhop, the last byte should not be zero:%s", s)
		}

		return &XHop{buf: buf}, nil
	}
}

func (x *XHop) Dup() *XHop {
	if x == nil {
		return NewXHop()
	}

	xhop := &XHop{
		buf: make([]byte, len(x.buf)),
	}
	copy(xhop.buf, x.buf)

	return xhop
}

func (x *XHop) Equal(x2 *XHop) bool {
	if x == nil || x2 == nil {
		return x == nil && x2 == nil
	} else {
		return bytes.Compare(x.buf, x2.buf) == 0 && x.c == x2.c
	}
}

//String should only be used for human reading.
//The func does not take performance into account.
//little endian
func (x *XHop) String() string {
	if x == nil {
		return ""
	}

	var (
		s = make([]byte, 0, 9*len(x.buf)-1) //8*N + N-1
		u uint8
	)
	for i := len(x.buf) - 1; i >= 0; i-- {
		u = 128
		for u > 0 {
			if u&x.buf[i] == u {
				s = append(s, '1')
			} else {
				s = append(s, '0')

			}
			u >>= 1
		}
		s = append(s, ' ') //add extra space for human reading
	}

	return string(s[:len(s)-1]) //strip the last extra space
}

//to hex
func (x *XHop) Hex() string {
	if x == nil {
		return ""
	}

	//create a new buf in BigEndian
	var buf = make([]byte, len(x.buf))
	for i := 0; i < len(buf); i++ {
		buf[i] = x.buf[len(buf)-1-i]
	}

	return hex.EncodeToString(buf)
}

//to decimal
func (x *XHop) Decimal() int64 {
	if x == nil {
		return 0
	}

	//create a new buf in BigEndian
	var buf = make([]byte, len(x.buf))
	for i := 0; i < len(buf); i++ {
		buf[i] = x.buf[len(buf)-1-i]
	}

	hexRes := hex.EncodeToString(buf)
	res, err := strconv.ParseInt(hexRes, 16, 64)
	if err != nil {
		panic(err.Error())
	}
	return res
}

// 1. strip the leftmost positive bit.
// 2. strip the leading zero bytes before 1.
//little endian
func (x *XHop) Parent() *XHop {
	if x == nil {
		return NewXHop()
	}

	//Root XHop's Parent is always itself.
	if x.IsRootXHop() {
		return x.Dup()
	}

	//attention: x.buf is in LittleEndian!
	xhop := x.Dup()
	var b = x.buf[len(x.buf)-1]
	xhop.buf[len(x.buf)-1] = b & ^(1 << (leftMostBitPos(b) - 1))

	//strip the left zero bytes
	n := len(xhop.buf) - 1
	for n > 0 {
		if xhop.buf[n] != 0 {
			break
		}
		n -= 1
	}

	xhop.buf = xhop.buf[:n+1]
	return xhop
}

//pading 100...0(seq 0) the x
func (x *XHop) Next() *XHop {
	if x == nil {
		return NewXHop().Next()
	}

	xhop := x.Dup()

	var (
		b  = x.buf[len(x.buf)-1]
		bp = uint64(leftMostBitPos(b)) //1~8
	)

	xhop.c = x.c

	if xhop.c >= 8-bp {
		xhop.c -= 8 - bp
		leading := make([]byte, xhop.c/8) //zero bytes
		leading = append(leading, 1<<(xhop.c%8))
		xhop.buf = append(xhop.buf, leading...)
	} else {
		xhop.buf[len(x.buf)-1] += (1 << xhop.c) << bp
	}

	//update counter
	xhop.c = 0
	x.c += 1

	return xhop
}

//pading 100...0(n 0 in total) the x
//different to Next(), NextN do not touch x.c
//use to current node rpc count add one(param is c.n)
func (x *XHop) NextN(n uint64) *XHop {
	if x == nil {
		return NewXHop().NextN(n)
	}

	xhop := x.Dup()
	xhop.c = n
	return xhop.Next()
}

//https://leetcode.com/problems/counting-bits/#/description
//获得当前节点等级(遍历byte数组,分别判断每个)
func (x *XHop) Hierarchy() (n int) {
	if x == nil {
		return
	}

	for _, b := range x.buf {
		n += byteBits(b)
	}

	return
}

//返回byte数中1的位数
func byteBits(b byte) (c int) {
	n := uint8(b)
	for n > 0 {
		n &= (n - 1)
		c += 1
	}

	return
}

func leftMostBitPos(b byte) uint8 {
	//11111111
	//8...1

	var (
		p   uint8 = 128
		pos uint8 = 8
	)

	for p > 0 {
		if b&p == p {
			break
		}

		p >>= 1
		pos -= 1
	}

	return pos
}

func (x *XHop) GetCount() uint64 {
	return x.c
}

func (node *XHop)GetNext() *XHop {
	return node.NextN(node.c)
}

func Test(){
	node := NewXHop()
	fmt.Println(node)
	fmt.Println(node.Next()) //当前node第1次调用
	fmt.Println(node.NextN(1))	//当前node第2次调用
	fmt.Println(node.NextN(2))	//当前node第3次调用
}

uuid:https://blog.csdn.net/why444216978/article/details/104173824

你可能感兴趣的:(go)