@ -6,9 +6,10 @@ import (
"github.com/go-kratos/kratos/v2/middleware"
"github.com/go-kratos/kratos/v2/transport/grpc"
"github.com/go-kratos/kratos/v2/transport/http"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/opentracing/opentracing-go/log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
oteltrace "go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/metadata"
)
@ -16,61 +17,92 @@ import (
type Option func ( * options )
type options struct {
tracer opentracing . Tracer
TracerProvider oteltrace . TracerProvider
Propagators propagation . TextMapPropagator
}
// WithTracer sets a custom tracer to be used for this middleware, otherwise the opentracing.GlobalTracer is used.
func WithTracer ( tracer opentracing . Tracer ) Option {
return func ( o * options ) {
o . tracer = tracer
func WithPropagators ( propagators propagation . TextMapPropagator ) Option {
return func ( opts * options ) {
opts . Propagators = propagators
}
}
// Server returns a new server middleware for OpenTracing.
func WithTracerProvider ( provider oteltrace . TracerProvider ) Option {
return func ( opts * options ) {
opts . TracerProvider = provider
}
}
type MetadataCarrier struct {
md * metadata . MD
}
var _ propagation . TextMapCarrier = & MetadataCarrier { }
func ( mc MetadataCarrier ) Get ( key string ) string {
values := mc . md . Get ( key )
if len ( values ) == 0 {
return ""
}
return values [ 0 ]
}
// Set stores the key-value pair.
func ( mc MetadataCarrier ) Set ( key string , value string ) {
mc . md . Set ( key , value )
}
// Set stores the key-value pair.
func ( mc MetadataCarrier ) Keys ( ) [ ] string {
keys := make ( [ ] string , 0 , mc . md . Len ( ) )
for key := range * mc . md {
keys = append ( keys , key )
}
return keys
}
// Server returns a new server middleware for OpenTelemetry.
func Server ( opts ... Option ) middleware . Middleware {
options := options {
tracer : opentracing . GlobalTracer ( ) ,
TracerProvider : otel . GetTracerProvider ( ) ,
Propagators : otel . GetTextMapPropagator ( ) ,
}
for _ , o := range opts {
o ( & options )
}
tracer := options . TracerProvider . Tracer ( "server" )
return func ( handler middleware . Handler ) middleware . Handler {
return func ( ctx context . Context , req interface { } ) ( reply interface { } , err error ) {
var (
component string
operation string
spanContext opentracing . SpanContext
)
if info , ok := http . FromServerContext ( ctx ) ; ok {
// HTTP span
component = "HTTP"
operation = info . Request . RequestURI
spanContext , _ = options . tracer . Extract (
opentracing . HTTPHeaders ,
opentracing . HTTPHeadersCarrier ( info . Request . Header ) ,
)
ctx = propagation . NewCompositeTextMapPropagator ( options . Propagators ) .
Extract ( ctx , propagation . HeaderCarrier ( info . Request . Header ) )
} else if info , ok := grpc . FromServerContext ( ctx ) ; ok {
// gRPC span
component = "gRPC"
operation = info . FullMethod
if md , ok := metadata . FromIncomingContext ( ctx ) ; ok {
spanContext , _ = options . tracer . Extract (
opentracing . HTTPHeaders ,
opentracing . HTTPHeadersCarrier ( md ) ,
)
ctx = propagation . NewCompositeTextMapPropagator ( options . Propagators ) .
Extract ( ctx , MetadataCarrier { md : & md } )
}
}
span := options . tracer . StartSpan (
ctx , span := tracer . Start ( ctx ,
operation ,
ext . RPCServerOption ( spanContext ) ,
opentracing . Tag { Key : string ( ext . Component ) , Value : component } ,
oteltrace . WithAttributes ( attribute . String ( "component" , component ) ) ,
oteltrace . WithSpanKind ( oteltrace . SpanKindServer ) ,
)
defer span . Finish ( )
defer span . End ( )
if reply , err = handler ( ctx , req ) ; err != nil {
ext . Error . Set ( span , true )
span . LogField s(
log . String ( "event" , "error" ) ,
log . String ( "message" , err . Error ( ) ) ,
span . RecordError ( err )
span . SetAttribute s(
attribute . String ( "event" , "error" ) ,
attribute . String ( "message" , err . Error ( ) ) ,
)
}
return
@ -78,52 +110,51 @@ func Server(opts ...Option) middleware.Middleware {
}
}
// Client returns a new client middleware for OpenTracing .
// Client returns a new client middleware for OpenTelemetry .
func Client ( opts ... Option ) middleware . Middleware {
options := options { }
options := options {
TracerProvider : otel . GetTracerProvider ( ) ,
Propagators : otel . GetTextMapPropagator ( ) ,
}
for _ , o := range opts {
o ( & options )
}
tracer := options . TracerProvider . Tracer ( "client" )
return func ( handler middleware . Handler ) middleware . Handler {
return func ( ctx context . Context , req interface { } ) ( reply interface { } , err error ) {
var (
component string
operation string
parent opentracing . SpanContext
carrier opentracing . HTTPHeadersCarrier
carrier propagation . TextMapCarrier
)
if span := opentracing . SpanFromContext ( ctx ) ; span != nil {
parent = span . Context ( )
}
if info , ok := http . FromClientContext ( ctx ) ; ok {
// HTTP span
component = "HTTP"
operation = info . Request . RequestURI
carrier = opentracing . HTTPHeaders Carrier( info . Request . Header )
carrier = propagation . Header Carrier( info . Request . Header )
} else if info , ok := grpc . FromClientContext ( ctx ) ; ok {
// gRPC span
component = "gRPC"
operation = info . FullMethod
if md , ok := metadata . FromOutgoingContext ( ctx ) ; ok {
carrier = opentracing . HTTPHeadersCarrier ( md )
ctx = metadata . NewOutgoingContext ( ctx , md )
} else {
md , ok := metadata . FromOutgoingContext ( ctx )
if ! ok {
md = metadata . Pairs ( )
carrier = opentracing . HTTPHeadersCarrier ( md )
ctx = metadata . NewOutgoingContext ( ctx , md )
}
carrier = MetadataCarrier { md : & md }
ctx = metadata . NewOutgoingContext ( ctx , md )
}
span := options . tracer . StartSpan (
ctx , span := tracer . Start ( ctx ,
operation ,
opentracing . ChildOf ( parent ) ,
ex t . SpanKindRPC Client ,
oteltrace . WithAttributes ( attribute . String ( "component" , component ) ) ,
ot eltrace . WithSpanKind ( o teltrace . SpanKindClient ) ,
)
defer span . Finish ( )
options . tracer . Inject ( span . Context ( ) , opentracing . HTTPHeaders , carrier )
ctx = opentracing . ContextWithSpan ( ctx , span )
// send handler
defer span . End ( )
propagation . NewCompositeTextMapPropagator ( options . Propagators ) . Inject ( ctx , carrier )
if reply , err = handler ( ctx , req ) ; err != nil {
ext . Error . Set ( span , true )
span . LogField s(
log . String ( "event" , "error" ) ,
log . String ( "message" , err . Error ( ) ) ,
span . RecordError ( err )
span . SetAttribute s(
attribute . String ( "event" , "error" ) ,
attribute . String ( "message" , err . Error ( ) ) ,
)
}
return