feat: enhance tracing (#1300)

* feat: enhance tracing

* fix go lint
pull/1302/head
longxboy 3 years ago committed by GitHub
parent 2fa9168bde
commit f65a0a9134
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      examples/go.mod
  2. 9
      examples/go.sum
  3. 32
      examples/traces/app/message/main.go
  4. 41
      examples/traces/app/user/main.go
  5. 6
      go.mod
  6. 15
      go.sum
  7. 43
      middleware/tracing/metadata.go
  8. 144
      middleware/tracing/span.go
  9. 39
      middleware/tracing/statsHandler.go
  10. 45
      middleware/tracing/tracer.go
  11. 19
      middleware/tracing/tracing.go
  12. 8
      middleware/tracing/tracing_test.go

@ -32,10 +32,10 @@ require (
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
go.etcd.io/etcd/client/v3 v3.5.0 go.etcd.io/etcd/client/v3 v3.5.0
go.opentelemetry.io/otel v1.0.0-RC1 go.opentelemetry.io/otel v1.0.0-RC2
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1 go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC2
go.opentelemetry.io/otel/sdk v1.0.0-RC1 go.opentelemetry.io/otel/sdk v1.0.0-RC2
go.opentelemetry.io/otel/trace v1.0.0-RC1 go.opentelemetry.io/otel/trace v1.0.0-RC2
go.uber.org/zap v1.18.1 go.uber.org/zap v1.18.1
golang.org/x/text v0.3.6 golang.org/x/text v0.3.6
google.golang.org/genproto v0.0.0-20210701191553-46259e63a0a9 google.golang.org/genproto v0.0.0-20210701191553-46259e63a0a9

@ -709,8 +709,12 @@ go.opentelemetry.io/otel v0.17.0/go.mod h1:Oqtdxmf7UtEvL037ohlgnaYa1h7GtMh0NcSd9
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc= go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc=
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
go.opentelemetry.io/otel v1.0.0-RC2 h1:SHhxSjB+omnGZPgGlKe+QMp3MyazcOHdQ8qwo89oKbg=
go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM=
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1 h1:tVhw2BMSAk248rhdeirOe9hlXKwGHDvVtF7P8F+H2DU= go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1 h1:tVhw2BMSAk248rhdeirOe9hlXKwGHDvVtF7P8F+H2DU=
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1/go.mod h1:FXJnjGCoTQL6nQ8OpFJ0JI1DrdOvMoVx49ic0Hg4+D4= go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1/go.mod h1:FXJnjGCoTQL6nQ8OpFJ0JI1DrdOvMoVx49ic0Hg4+D4=
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC2 h1:RF0nWsIDpDBe+s06lkLxUw9CWQUAhO6hBSxxB7dz45s=
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC2/go.mod h1:sZZqN3Vb0iT+NE6mZ1S7sNyH3t4PFk6ElK5TLGFBZ7E=
go.opentelemetry.io/otel/internal/metric v0.21.0 h1:gZlIBo5O51hZOOZz8vEcuRx/l5dnADadKfpT70AELoo= go.opentelemetry.io/otel/internal/metric v0.21.0 h1:gZlIBo5O51hZOOZz8vEcuRx/l5dnADadKfpT70AELoo=
go.opentelemetry.io/otel/internal/metric v0.21.0/go.mod h1:iOfAaY2YycsXfYD4kaRSbLx2LKmfpKObWBEv9QK5zFo= go.opentelemetry.io/otel/internal/metric v0.21.0/go.mod h1:iOfAaY2YycsXfYD4kaRSbLx2LKmfpKObWBEv9QK5zFo=
go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0= go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0=
@ -723,10 +727,14 @@ go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0c
go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4= go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4=
go.opentelemetry.io/otel/sdk v1.0.0-RC1 h1:Sy2VLOOg24bipyC29PhuMXYNJrLsxkie8hyI7kUlG9Q= go.opentelemetry.io/otel/sdk v1.0.0-RC1 h1:Sy2VLOOg24bipyC29PhuMXYNJrLsxkie8hyI7kUlG9Q=
go.opentelemetry.io/otel/sdk v1.0.0-RC1/go.mod h1:kj6yPn7Pgt5ByRuwesbaWcRLA+V7BSDg3Hf8xRvsvf8= go.opentelemetry.io/otel/sdk v1.0.0-RC1/go.mod h1:kj6yPn7Pgt5ByRuwesbaWcRLA+V7BSDg3Hf8xRvsvf8=
go.opentelemetry.io/otel/sdk v1.0.0-RC2 h1:ROuteeSCBaZNjiT9JcFzZepmInDvLktR28Y6qKo8bCs=
go.opentelemetry.io/otel/sdk v1.0.0-RC2/go.mod h1:fgwHyiDn4e5k40TD9VX243rOxXR+jzsWBZYA2P5jpEw=
go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg= go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg=
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58= go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58=
go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg= go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg=
go.opentelemetry.io/otel/trace v1.0.0-RC2 h1:dunAP0qDULMIT82atj34m5RgvsIK6LcsXf1c/MsYg1w=
go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@ -922,6 +930,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=

@ -12,12 +12,12 @@ import (
"github.com/go-kratos/kratos/v2/middleware/recovery" "github.com/go-kratos/kratos/v2/middleware/recovery"
"github.com/go-kratos/kratos/v2/middleware/tracing" "github.com/go-kratos/kratos/v2/middleware/tracing"
"github.com/go-kratos/kratos/v2/transport/grpc" "github.com/go-kratos/kratos/v2/transport/grpc"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
tracesdk "go.opentelemetry.io/otel/sdk/trace" tracesdk "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0" semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.opentelemetry.io/otel/trace"
) )
// go build -ldflags "-X main.Version=x.y.z" // go build -ldflags "-X main.Version=x.y.z"
@ -31,28 +31,28 @@ var (
// server is used to implement helloworld.GreeterServer. // server is used to implement helloworld.GreeterServer.
type server struct { type server struct {
v1.UnimplementedMessageServiceServer v1.UnimplementedMessageServiceServer
tracer trace.TracerProvider
} }
// Get trace provider // set trace provider
func tracerProvider(url string) (*tracesdk.TracerProvider, error) { func setTracerProvider(url string) error {
// Create the Jaeger exporter // Create the Jaeger exporter
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url))) exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil { if err != nil {
return nil, err return err
} }
tp := tracesdk.NewTracerProvider( tp := tracesdk.NewTracerProvider(
tracesdk.WithSampler(tracesdk.AlwaysSample()), // Set the sampling rate based on the parent span to 100%
tracesdk.WithSampler(tracesdk.ParentBased(tracesdk.TraceIDRatioBased(1.0))),
// Always be sure to batch in production. // Always be sure to batch in production.
tracesdk.WithBatcher(exp), tracesdk.WithBatcher(exp),
// Record information about this application in an Resource. // Record information about this application in an Resource.
tracesdk.WithResource(resource.NewSchemaless( tracesdk.WithResource(resource.NewSchemaless(
semconv.ServiceNameKey.String(v1.MessageService_ServiceDesc.ServiceName), semconv.ServiceNameKey.String(Name),
attribute.String("environment", "development"), attribute.String("env", "dev"),
attribute.Int64("ID", 1),
)), )),
) )
return tp, nil otel.SetTracerProvider(tp)
return nil
} }
func (s *server) GetUserMessage(ctx context.Context, request *v1.GetUserMessageRequest) (*v1.GetUserMessageReply, error) { func (s *server) GetUserMessage(ctx context.Context, request *v1.GetUserMessageRequest) (*v1.GetUserMessageReply, error) {
@ -69,12 +69,16 @@ func main() {
logger = log.With(logger, "span_id", log.SpanID()) logger = log.With(logger, "span_id", log.SpanID())
log := log.NewHelper(logger) log := log.NewHelper(logger)
tp, err := tracerProvider("http://jaeger:14268/api/traces") url := "http://jaeger:14268/api/traces"
if os.Getenv("jaeger_url") != "" {
url = os.Getenv("jaeger_url")
}
err := setTracerProvider(url)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
s := &server{tracer: tp} s := &server{}
// grpc server // grpc server
grpcSrv := grpc.NewServer( grpcSrv := grpc.NewServer(
grpc.Address(":9000"), grpc.Address(":9000"),
@ -82,9 +86,7 @@ func main() {
middleware.Chain( middleware.Chain(
recovery.Recovery(), recovery.Recovery(),
// Configuring tracing Middleware // Configuring tracing Middleware
tracing.Server( tracing.Server(),
tracing.WithTracerProvider(tp),
),
logging.Server(logger), logging.Server(logger),
), ),
)) ))

@ -14,12 +14,14 @@ import (
"github.com/go-kratos/kratos/v2/middleware/tracing" "github.com/go-kratos/kratos/v2/middleware/tracing"
"github.com/go-kratos/kratos/v2/transport/grpc" "github.com/go-kratos/kratos/v2/transport/grpc"
"github.com/go-kratos/kratos/v2/transport/http" "github.com/go-kratos/kratos/v2/transport/http"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
tracesdk "go.opentelemetry.io/otel/sdk/trace" tracesdk "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0" semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.opentelemetry.io/otel/trace" grpcx "google.golang.org/grpc"
) )
// go build -ldflags "-X main.Version=x.y.z" // go build -ldflags "-X main.Version=x.y.z"
@ -33,41 +35,42 @@ var (
// server is used to implement helloworld.GreeterServer. // server is used to implement helloworld.GreeterServer.
type server struct { type server struct {
v1.UnimplementedUserServer v1.UnimplementedUserServer
tracer trace.TracerProvider
} }
// Get trace provider // Set global trace provider
func tracerProvider(url string) (*tracesdk.TracerProvider, error) { func setTracerProvider(url string) error {
// Create the Jaeger exporter // Create the Jaeger exporter
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url))) exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil { if err != nil {
return nil, err return err
} }
tp := tracesdk.NewTracerProvider( tp := tracesdk.NewTracerProvider(
tracesdk.WithSampler(tracesdk.AlwaysSample()), // Set the sampling rate based on the parent span to 100%
tracesdk.WithSampler(tracesdk.ParentBased(tracesdk.TraceIDRatioBased(1.0))),
// Always be sure to batch in production. // Always be sure to batch in production.
tracesdk.WithBatcher(exp), tracesdk.WithBatcher(exp),
// Record information about this application in an Resource. // Record information about this application in an Resource.
tracesdk.WithResource(resource.NewSchemaless( tracesdk.WithResource(resource.NewSchemaless(
semconv.ServiceNameKey.String(v1.User_ServiceDesc.ServiceName), semconv.ServiceNameKey.String(Name),
attribute.String("environment", "development"), attribute.String("env", "dev"),
attribute.Int64("ID", 1),
)), )),
) )
return tp, nil otel.SetTracerProvider(tp)
return nil
} }
func (s *server) GetMyMessages(ctx context.Context, in *v1.GetMyMessagesRequest) (*v1.GetMyMessagesReply, error) { func (s *server) GetMyMessages(ctx context.Context, in *v1.GetMyMessagesRequest) (*v1.GetMyMessagesReply, error) {
// create grpc conn // create grpc conn
// only for demo, use single instance in production env
conn, err := grpc.DialInsecure(ctx, conn, err := grpc.DialInsecure(ctx,
grpc.WithEndpoint("127.0.0.1:9000"), grpc.WithEndpoint("127.0.0.1:9000"),
grpc.WithMiddleware( grpc.WithMiddleware(
recovery.Recovery(), recovery.Recovery(),
tracing.Client( tracing.Client(),
tracing.WithTracerProvider(s.tracer),
),
), ),
grpc.WithTimeout(2*time.Second), grpc.WithTimeout(2*time.Second),
// for tracing remote ip recording
grpc.WithOptions(grpcx.WithStatsHandler(&tracing.ClientHandler{})),
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -91,7 +94,11 @@ func main() {
logger = log.With(logger, "span_id", log.SpanID()) logger = log.With(logger, "span_id", log.SpanID())
log := log.NewHelper(logger) log := log.NewHelper(logger)
tp, err := tracerProvider("http://jaeger:14268/api/traces") url := "http://jaeger:14268/api/traces"
if os.Getenv("jaeger_url") != "" {
url = os.Getenv("jaeger_url")
}
err := setTracerProvider(url)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
@ -101,13 +108,11 @@ func main() {
http.Middleware( http.Middleware(
recovery.Recovery(), recovery.Recovery(),
// Configuring tracing middleware // Configuring tracing middleware
tracing.Server( tracing.Server(),
tracing.WithTracerProvider(tp),
),
logging.Server(logger), logging.Server(logger),
), ),
) )
s := &server{tracer: tp} s := &server{}
v1.RegisterUserHTTPServer(httpSrv, s) v1.RegisterUserHTTPServer(httpSrv, s)
app := kratos.New( app := kratos.New(

@ -9,9 +9,9 @@ require (
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/imdario/mergo v0.3.12 github.com/imdario/mergo v0.3.12
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
go.opentelemetry.io/otel v1.0.0-RC1 go.opentelemetry.io/otel v1.0.0-RC2
go.opentelemetry.io/otel/sdk v1.0.0-RC1 go.opentelemetry.io/otel/sdk v1.0.0-RC2
go.opentelemetry.io/otel/trace v1.0.0-RC1 go.opentelemetry.io/otel/trace v1.0.0-RC2
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
google.golang.org/genproto v0.0.0-20210629200056-84d6f6074151 google.golang.org/genproto v0.0.0-20210629200056-84d6f6074151
google.golang.org/grpc v1.39.0 google.golang.org/grpc v1.39.0

@ -64,14 +64,12 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc= go.opentelemetry.io/otel v1.0.0-RC2 h1:SHhxSjB+omnGZPgGlKe+QMp3MyazcOHdQ8qwo89oKbg=
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM=
go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk= go.opentelemetry.io/otel/sdk v1.0.0-RC2 h1:ROuteeSCBaZNjiT9JcFzZepmInDvLktR28Y6qKo8bCs=
go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4= go.opentelemetry.io/otel/sdk v1.0.0-RC2/go.mod h1:fgwHyiDn4e5k40TD9VX243rOxXR+jzsWBZYA2P5jpEw=
go.opentelemetry.io/otel/sdk v1.0.0-RC1 h1:Sy2VLOOg24bipyC29PhuMXYNJrLsxkie8hyI7kUlG9Q= go.opentelemetry.io/otel/trace v1.0.0-RC2 h1:dunAP0qDULMIT82atj34m5RgvsIK6LcsXf1c/MsYg1w=
go.opentelemetry.io/otel/sdk v1.0.0-RC1/go.mod h1:kj6yPn7Pgt5ByRuwesbaWcRLA+V7BSDg3Hf8xRvsvf8= go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4=
go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58=
go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -108,6 +106,7 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

@ -0,0 +1,43 @@
package tracing
import (
"context"
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/metadata"
"go.opentelemetry.io/otel/propagation"
)
const serviceHeader = "x-md-service-name"
// Metadata is tracing metadata propagator
type Metadata struct{}
var _ propagation.TextMapPropagator = Metadata{}
// Inject sets metadata key-values from ctx into the carrier.
func (b Metadata) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
app, _ := kratos.FromContext(ctx)
carrier.Set(serviceHeader, app.Name())
}
// Extract returns a copy of parent with the metadata from the carrier added.
func (b Metadata) Extract(parent context.Context, carrier propagation.TextMapCarrier) context.Context {
name := carrier.Get(serviceHeader)
if name != "" {
if md, ok := metadata.FromServerContext(parent); ok {
md.Set(serviceHeader, name)
} else {
md := metadata.New()
md.Set(serviceHeader, name)
parent = metadata.NewServerContext(parent, md)
}
}
return parent
}
// Fields returns the keys who's values are set with Inject.
func (b Metadata) Fields() []string {
return []string{serviceHeader}
}

@ -0,0 +1,144 @@
package tracing
import (
"context"
"net"
"net/url"
"strings"
"github.com/go-kratos/kratos/v2/metadata"
"github.com/go-kratos/kratos/v2/transport"
"github.com/go-kratos/kratos/v2/transport/http"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/peer"
"google.golang.org/protobuf/proto"
)
func setClientSpan(ctx context.Context, span trace.Span, m interface{}) {
attrs := []attribute.KeyValue{}
var remote string
var operation string
var rpcKind string
if tr, ok := transport.FromClientContext(ctx); ok {
operation = tr.Operation()
rpcKind = tr.Kind().String()
if tr.Kind() == transport.KindHTTP {
if ht, ok := tr.(*http.Transport); ok {
method := ht.Request().Method
route := ht.PathTemplate()
path := ht.Request().URL.Path
attrs = append(attrs, semconv.HTTPMethodKey.String(method))
attrs = append(attrs, semconv.HTTPRouteKey.String(route))
attrs = append(attrs, semconv.HTTPTargetKey.String(path))
remote = ht.Request().Host
}
} else if tr.Kind() == transport.KindGRPC {
remote, _ = parseTarget(tr.Endpoint())
}
}
attrs = append(attrs, semconv.RPCSystemKey.String(rpcKind))
_, mAttrs := parseFullMethod(operation)
attrs = append(attrs, mAttrs...)
if remote != "" {
attrs = append(attrs, peerAttr(remote)...)
}
if p, ok := m.(proto.Message); ok {
attrs = append(attrs, attribute.Key("send_msg.size").Int(proto.Size(p)))
}
span.SetAttributes(attrs...)
}
func setServerSpan(ctx context.Context, span trace.Span, m interface{}) {
attrs := []attribute.KeyValue{}
var remote string
var operation string
var rpcKind string
if tr, ok := transport.FromServerContext(ctx); ok {
operation = tr.Operation()
rpcKind = tr.Kind().String()
if tr.Kind() == transport.KindHTTP {
if ht, ok := tr.(*http.Transport); ok {
method := ht.Request().Method
route := ht.PathTemplate()
path := ht.Request().URL.Path
attrs = append(attrs, semconv.HTTPMethodKey.String(method))
attrs = append(attrs, semconv.HTTPRouteKey.String(route))
attrs = append(attrs, semconv.HTTPTargetKey.String(path))
remote = ht.Request().RemoteAddr
}
} else if tr.Kind() == transport.KindGRPC {
if p, ok := peer.FromContext(ctx); ok {
remote = p.Addr.String()
}
}
}
attrs = append(attrs, semconv.RPCSystemKey.String(rpcKind))
_, mAttrs := parseFullMethod(operation)
attrs = append(attrs, mAttrs...)
attrs = append(attrs, peerAttr(remote)...)
if p, ok := m.(proto.Message); ok {
attrs = append(attrs, attribute.Key("recv_msg.size").Int(proto.Size(p)))
}
if md, ok := metadata.FromServerContext(ctx); ok {
attrs = append(attrs, semconv.PeerServiceKey.String(md.Get(serviceHeader)))
}
span.SetAttributes(attrs...)
}
// parseFullMethod returns a span name following the OpenTelemetry semantic
// conventions as well as all applicable span attribute.KeyValue attributes based
// on a gRPC's FullMethod.
func parseFullMethod(fullMethod string) (string, []attribute.KeyValue) {
name := strings.TrimLeft(fullMethod, "/")
parts := strings.SplitN(name, "/", 2)
if len(parts) != 2 {
// Invalid format, does not follow `/package.service/method`.
return name, []attribute.KeyValue{attribute.Key("rpc.operation").String(fullMethod)}
}
var attrs []attribute.KeyValue
if service := parts[0]; service != "" {
attrs = append(attrs, semconv.RPCServiceKey.String(service))
}
if method := parts[1]; method != "" {
attrs = append(attrs, semconv.RPCMethodKey.String(method))
}
return name, attrs
}
// peerAttr returns attributes about the peer address.
func peerAttr(addr string) []attribute.KeyValue {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return []attribute.KeyValue(nil)
}
if host == "" {
host = "127.0.0.1"
}
return []attribute.KeyValue{
semconv.NetPeerIPKey.String(host),
semconv.NetPeerPortKey.String(port),
}
}
func parseTarget(endpoint string) (address string, err error) {
var u *url.URL
u, err = url.Parse(endpoint)
if err != nil {
if u, err = url.Parse("http://" + endpoint); err != nil {
return "", err
}
return u.Host, nil
}
if len(u.Path) > 1 {
return u.Path[1:], nil
}
return endpoint, nil
}

@ -0,0 +1,39 @@
package tracing
import (
"context"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/stats"
)
// ClientHandler is tracing ClientHandler
type ClientHandler struct {
}
// HandleConn exists to satisfy gRPC stats.Handler.
func (c *ClientHandler) HandleConn(ctx context.Context, cs stats.ConnStats) {
}
// TagConn exists to satisfy gRPC stats.Handler.
func (c *ClientHandler) TagConn(ctx context.Context, cti *stats.ConnTagInfo) context.Context {
return ctx
}
// HandleRPC implements per-RPC tracing and stats instrumentation.
func (c *ClientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
if _, ok := rs.(*stats.OutHeader); ok {
if p, ok := peer.FromContext(ctx); ok {
remoteAddr := p.Addr.String()
if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() {
span.SetAttributes(peerAttr(remoteAddr)...)
}
}
}
}
// TagRPC implements per-RPC context management.
func (c *ClientHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context {
return ctx
}

@ -4,70 +4,77 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/go-kratos/kratos/v2/errors"
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
"google.golang.org/protobuf/proto"
) )
// Tracer is otel span tracer // Tracer is otel span tracer
type Tracer struct { type Tracer struct {
tracer trace.Tracer tracer trace.Tracer
kind trace.SpanKind kind trace.SpanKind
opt *options
} }
// NewTracer create tracer instance // NewTracer create tracer instance
func NewTracer(kind trace.SpanKind, opts ...Option) *Tracer { func NewTracer(kind trace.SpanKind, opts ...Option) *Tracer {
options := options{} options := options{
propagator: propagation.NewCompositeTextMapPropagator(Metadata{}, propagation.Baggage{}, propagation.TraceContext{}),
}
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }
if options.TracerProvider != nil { if options.tracerProvider != nil {
otel.SetTracerProvider(options.TracerProvider) otel.SetTracerProvider(options.tracerProvider)
}
if options.Propagator != nil {
otel.SetTextMapPropagator(options.Propagator)
} else {
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{}))
} }
switch kind { switch kind {
case trace.SpanKindClient: case trace.SpanKindClient:
return &Tracer{tracer: otel.Tracer("client"), kind: kind} return &Tracer{tracer: otel.Tracer("kartos"), kind: kind, opt: &options}
case trace.SpanKindServer: case trace.SpanKindServer:
return &Tracer{tracer: otel.Tracer("server"), kind: kind} return &Tracer{tracer: otel.Tracer("kartos"), kind: kind, opt: &options}
default: default:
panic(fmt.Sprintf("unsupported span kind: %v", kind)) panic(fmt.Sprintf("unsupported span kind: %v", kind))
} }
} }
// Start start tracing span // Start start tracing span
func (t *Tracer) Start(ctx context.Context, component string, operation string, carrier propagation.TextMapCarrier) (context.Context, trace.Span) { func (t *Tracer) Start(ctx context.Context, operation string, carrier propagation.TextMapCarrier) (context.Context, trace.Span) {
if t.kind == trace.SpanKindServer { if t.kind == trace.SpanKindServer {
ctx = otel.GetTextMapPropagator().Extract(ctx, carrier) ctx = t.opt.propagator.Extract(ctx, carrier)
} }
ctx, span := t.tracer.Start(ctx, ctx, span := t.tracer.Start(ctx,
operation, operation,
trace.WithAttributes(attribute.String("component", component)),
trace.WithSpanKind(t.kind), trace.WithSpanKind(t.kind),
) )
if t.kind == trace.SpanKindClient { if t.kind == trace.SpanKindClient {
otel.GetTextMapPropagator().Inject(ctx, carrier) t.opt.propagator.Inject(ctx, carrier)
} }
return ctx, span return ctx, span
} }
// End finish tracing span // End finish tracing span
func (t *Tracer) End(ctx context.Context, span trace.Span, err error) { func (t *Tracer) End(ctx context.Context, span trace.Span, m interface{}, err error) {
if err != nil { if err != nil {
span.RecordError(err) span.RecordError(err)
span.SetAttributes( if e := errors.FromError(err); e != nil {
attribute.String("event", "error"), span.SetAttributes(attribute.Key("rpc.status_code").Int64(int64(e.Code)))
attribute.String("message", err.Error()), }
)
span.SetStatus(codes.Error, err.Error()) span.SetStatus(codes.Error, err.Error())
} else { } else {
span.SetStatus(codes.Ok, "OK") span.SetStatus(codes.Ok, "OK")
} }
if p, ok := m.(proto.Message); ok {
if t.kind == trace.SpanKindServer {
span.SetAttributes(attribute.Key("send_msg.size").Int(proto.Size(p)))
} else {
span.SetAttributes(attribute.Key("recv_msg.size").Int(proto.Size(p)))
}
}
span.End() span.End()
} }

@ -13,21 +13,22 @@ import (
type Option func(*options) type Option func(*options)
type options struct { type options struct {
TracerProvider trace.TracerProvider tracerProvider trace.TracerProvider
Propagator propagation.TextMapPropagator propagator propagation.TextMapPropagator
} }
// WithPropagator with tracer propagator. // WithPropagator with tracer propagator.
func WithPropagator(propagator propagation.TextMapPropagator) Option { func WithPropagator(propagator propagation.TextMapPropagator) Option {
return func(opts *options) { return func(opts *options) {
opts.Propagator = propagator opts.propagator = propagator
} }
} }
// WithTracerProvider with tracer provider. // WithTracerProvider with tracer provider.
// Deprecated: use otel.SetTracerProvider(provider) instead.
func WithTracerProvider(provider trace.TracerProvider) Option { func WithTracerProvider(provider trace.TracerProvider) Option {
return func(opts *options) { return func(opts *options) {
opts.TracerProvider = provider opts.tracerProvider = provider
} }
} }
@ -38,8 +39,9 @@ func Server(opts ...Option) middleware.Middleware {
return func(ctx context.Context, req interface{}) (reply interface{}, err error) { return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
if tr, ok := transport.FromServerContext(ctx); ok { if tr, ok := transport.FromServerContext(ctx); ok {
var span trace.Span var span trace.Span
ctx, span = tracer.Start(ctx, tr.Kind().String(), tr.Operation(), tr.RequestHeader()) ctx, span = tracer.Start(ctx, tr.Operation(), tr.RequestHeader())
defer func() { tracer.End(ctx, span, err) }() setServerSpan(ctx, span, req)
defer func() { tracer.End(ctx, span, reply, err) }()
} }
return handler(ctx, req) return handler(ctx, req)
} }
@ -53,8 +55,9 @@ func Client(opts ...Option) middleware.Middleware {
return func(ctx context.Context, req interface{}) (reply interface{}, err error) { return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
if tr, ok := transport.FromClientContext(ctx); ok { if tr, ok := transport.FromClientContext(ctx); ok {
var span trace.Span var span trace.Span
ctx, span = tracer.Start(ctx, tr.Kind().String(), tr.Operation(), tr.RequestHeader()) ctx, span = tracer.Start(ctx, tr.Operation(), tr.RequestHeader())
defer func() { tracer.End(ctx, span, err) }() setClientSpan(ctx, span, req)
defer func() { tracer.End(ctx, span, reply, err) }()
} }
return handler(ctx, req) return handler(ctx, req)
} }

@ -57,15 +57,15 @@ func TestTracing(t *testing.T) {
tracer := NewTracer(trace.SpanKindClient, WithTracerProvider(tp), WithPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{}))) tracer := NewTracer(trace.SpanKindClient, WithTracerProvider(tp), WithPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})))
ts := &Transport{kind: transport.KindHTTP, header: carrier} ts := &Transport{kind: transport.KindHTTP, header: carrier}
ctx, aboveSpan := tracer.Start(transport.NewClientContext(context.Background(), ts), ts.Kind().String(), ts.Operation(), ts.RequestHeader()) ctx, aboveSpan := tracer.Start(transport.NewClientContext(context.Background(), ts), ts.Operation(), ts.RequestHeader())
defer tracer.End(ctx, aboveSpan, nil) defer tracer.End(ctx, aboveSpan, nil, nil)
// server use Extract fetch traceInfo from carrier // server use Extract fetch traceInfo from carrier
tracer = NewTracer(trace.SpanKindServer, WithPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{}))) tracer = NewTracer(trace.SpanKindServer, WithPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})))
ts = &Transport{kind: transport.KindHTTP, header: carrier} ts = &Transport{kind: transport.KindHTTP, header: carrier}
ctx, span := tracer.Start(transport.NewServerContext(ctx, ts), ts.Kind().String(), ts.Operation(), ts.RequestHeader()) ctx, span := tracer.Start(transport.NewServerContext(ctx, ts), ts.Operation(), ts.RequestHeader())
defer tracer.End(ctx, span, nil) defer tracer.End(ctx, span, nil, nil)
if aboveSpan.SpanContext().TraceID() != span.SpanContext().TraceID() { if aboveSpan.SpanContext().TraceID() != span.SpanContext().TraceID() {
t.Fatalf("TraceID failed to deliver") t.Fatalf("TraceID failed to deliver")

Loading…
Cancel
Save