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:latestgo
来一个普通的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版权协议,转载请附上原文出处链接和本声明。