Go语言jaeger和opentracing

OpenTracing开放式分布式追踪规范,常见实现:jaeger和zipkin

docker启动一个实例:

docker run  -p 5775:5775/udp    -p 16686:16686    -p 6831:6831/udp   -p 6832:6832/udp   -p 5778:5778   -p 14268:14268    jaegertracing/all-in-one:latest

go 

来一个普通的go程序

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/uber/jaeger-client-go"
	"github.com/uber/jaeger-lib/metrics"

	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"github.com/uber/jaeger-client-go/config"
)

var (
	tracerServer opentracing.Tracer
)

func TraceInit(serviceName string, samplerType string, samplerParam float64) (opentracing.Tracer, io.Closer) {
	cfg := &config.Configuration{
		ServiceName: serviceName,
		Sampler: &config.SamplerConfig{
			Type:  samplerType,
			Param: samplerParam,
		},
		Reporter: &config.ReporterConfig{
			LocalAgentHostPort: "192.168.100.21:6831",
			LogSpans:           true,
		},
	}

	tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger), config.Metrics(metrics.NullFactory))
	if err != nil {
		panic(fmt.Sprintf("Init failed: %v\n", err))
	}

	return tracer, closer
}

func GetListProc(w http.ResponseWriter, req *http.Request) {
	spanCtx, _ := tracerServer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
	span := tracerServer.StartSpan("GetListProc", ext.RPCServerOption(spanCtx))
	defer span.Finish()

	fmt.Println("Get request getList")
	respList := []string{"l1", "l2", "l3", "l4", "l5"}
	respString := ""

	for _, v := range respList {
		respString += v + ","
	}

	fmt.Println(respString)
	io.WriteString(w, respString)
}

func sendRequest(req *http.Request) {
	resp, err := http.DefaultClient.Do(req)

	if err != nil {
		fmt.Printf("Do send requst failed(%s)\n", err)
		return
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("ReadAll error(%s)\n", err)
		return
	}

	if resp.StatusCode != 200 {
		return
	}
	fmt.Printf("Response:%s\n", string(body))

}

func main() {
	// server
	var closerServer io.Closer
	tracerServer, closerServer = TraceInit("Trace-Server", "const", 1)
	defer closerServer.Close()

	http.HandleFunc("/getList", GetListProc)

	go http.ListenAndServe(":8080", nil)

	//client
	tracerClient, closerClient := TraceInit("CS-tracing", "const", 1)
	defer closerClient.Close()

	opentracing.SetGlobalTracer(tracerClient)
	span := tracerClient.StartSpan("getlist trace")
	span.SetTag("trace to", "getlist")
	defer span.Finish()

	reqURL := "http://localhost:8080/getList"
	req, err := http.NewRequest("GET", reqURL, nil)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	span.Tracer().Inject(
		span.Context(),
		opentracing.HTTPHeaders,
		opentracing.HTTPHeadersCarrier(req.Header),
	)

	sendRequest(req)
}

运行效果:

访问http://192.168.100.21:16686/ 如下:

GRPC

我这里用以前的项目 https://github.com/dz45693/gogrpcjwt.git, 大家可以参考grpc 拦截器【 go 和 asp.net core的实现】 这里我引用了github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc 这个包

服务端代码修改如下:

客户端:

普通程序日志:

有些时候 我们的方法会有很多调用和日志, 我希望把这一次api请求的日志归纳在一起【elk可以通过某个字段过滤】, 这里我们可以在api 入口创建一个apn 绑定到context, 然后每个方法都传这个context,日志读取context的traceid

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"runtime"
	"strings"
	"time"

	"github.com/opentracing/opentracing-go"
	"github.com/uber/jaeger-client-go"
	"github.com/uber/jaeger-client-go/config"
	"github.com/uber/jaeger-lib/metrics"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func init() {
	tracerClient, closerClient := TraceInit("CS-tracing", "const", 1)
	defer closerClient.Close()

	opentracing.SetGlobalTracer(tracerClient)
}

func main() {
	span := opentracing.GlobalTracer().StartSpan("gavintest")
	ctx := opentracing.ContextWithSpan(context.Background(), span)
	log(ctx, "gavin test")
}

func log(ctx context.Context, format string, args ...interface{}) {
	traceId, spanId := GetTraceId(ctx)

	logerData := JsonLogger{
		TraceId:  traceId,
		SpanId:   spanId,
		Content:  fmt.Sprintf(format, args...),
		CallPath: GetCallPath(),
		Level:    zap.InfoLevel,
		LogTime:  time.Now().Format("2006-01-02T15:04:05.000+08:00"),
	}

	jsonData, _ := json.Marshal(logerData)

	fmt.Println("---------------------")
	fmt.Println(string(jsonData))
}

func TraceInit(serviceName string, samplerType string, samplerParam float64) (opentracing.Tracer, io.Closer) {
	cfg := &config.Configuration{
		ServiceName: serviceName,
		Sampler: &config.SamplerConfig{
			Type:  samplerType,
			Param: samplerParam,
		},
		Reporter: &config.ReporterConfig{
			LocalAgentHostPort: "192.168.100.30:6831",
			LogSpans:           true,
		},
	}

	tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger), config.Metrics(metrics.NullFactory))
	if err != nil {
		panic(fmt.Sprintf("Init failed: %v\n", err))
	}

	return tracer, closer
}

func GetTraceId(ctx context.Context) (string, uint64) {
	span := opentracing.SpanFromContext(ctx)
	if span == nil {
		return "", 0
	}

	if sc, ok := span.Context().(jaeger.SpanContext); ok {

		return fmt.Sprintf("%v", sc.TraceID()), uint64(sc.SpanID())
	}
	return "", 0
}

func GetCallPath() string {
	_, file, lineno, ok := runtime.Caller(2)
	if ok {
		return strings.Replace(fmt.Sprintf("%s:%d", stringTrim(file, ""), lineno), "%2e", ".", -1)
	}
	return ""
}

func stringTrim(s, cut string) string {
	ss := strings.SplitN(s, cut, 2)
	if len(ss) == 1 {
		return ss[0]
	}
	return ss[1]
}

type JsonLogger struct {
	TraceId  string        `json:"traceId"`
	SpanId   uint64        `json:"spanId"`
	Content  interface{}   `json:"content"`
	CallPath interface{}   `json:"callPath"`
	LogTime  string        `json:"logdate"`
	Level    zapcore.Level `json:"level"`
}

运行效果:


版权声明:本文为dz45693原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。