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 ", 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) ", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("ReadAll error(%s) ", err) return } if resp.StatusCode != 200 { return } fmt.Printf("Response:%s ", 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 ", 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"` }
运行效果: