package trace
import (
"net/http"
"net/url"
"time"
"why/log"
"github.com/gin-gonic/gin"
opentracing "github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
const defaultComponentName = "net/http"
type Options struct {
tracer opentracing.Tracer
opNameFunc func(r *http.Request) string
spanObserver func(span opentracing.Span, r *http.Request)
urlTagFunc func(u *url.URL) string
componentName string
}
type OptionsFunc func(*Options)
func OperationNameFunc(f func(r *http.Request) string) OptionsFunc {
return func(options *Options) {
options.opNameFunc = f
}
}
func WithTracer(tracer opentracing.Tracer) OptionsFunc {
return func(options *Options) {
options.tracer = tracer
}
}
func WithComponentName(componentName string) OptionsFunc {
return func(options *Options) {
options.componentName = componentName
}
}
func WithSpanObserver(f func(span opentracing.Span, r *http.Request)) OptionsFunc {
return func(options *Options) {
options.spanObserver = f
}
}
func WithURLTagFunc(f func(u *url.URL) string) OptionsFunc {
return func(options *Options) {
options.urlTagFunc = f
}
}
//OpenTracing 链路追踪中间件
//实现了[opentracing](https://opentracing.io)协议
//tracer默认使用jaeger.Tracer,如需修改,可用中间件WithTracer
func OpenTracing(serviceName string, options ...OptionsFunc) gin.HandlerFunc {
opts := Options{
opNameFunc: func(r *http.Request) string {
return r.Proto + " " + r.Method
},
spanObserver: func(span opentracing.Span, r *http.Request) {},
urlTagFunc: func(u *url.URL) string {
return u.String()
},
}
for _, opt := range options {
opt(&opts)
}
if opts.tracer == nil {
opts.tracer = opentracing.GlobalTracer()
}
return func(c *gin.Context) {
carrier := opentracing.HTTPHeadersCarrier(c.Request.Header)
spanContext, _ := opts.tracer.Extract(opentracing.HTTPHeaders, carrier)
op := opts.opNameFunc(c.Request)
sp := opts.tracer.StartSpan(op, opentracing.ChildOf(spanContext), opentracing.StartTime(time.Now()))
ext.HTTPMethod.Set(sp, c.Request.Method)
ext.HTTPUrl.Set(sp, opts.urlTagFunc(c.Request.URL))
opts.spanObserver(sp, c.Request)
componentName := opts.componentName
if componentName == "" {
componentName = defaultComponentName
}
ext.Component.Set(sp, componentName)
c.Request = c.Request.WithContext(
opentracing.ContextWithSpan(c.Request.Context(), sp))
//trace info 注入resp.Header
sp.Tracer().Inject(sp.Context(), opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(c.Writer.Header()))
c.Next()
ext.HTTPStatusCode.Set(sp, uint16(c.Writer.Status()))
sp.FinishWithOptions(opentracing.FinishOptions{FinishTime: time.Now()})
}
}
type TraceInjecter struct {}
//rpc调用时,trace注入header
func (injecter *TraceInjecter)InjectTrace(c *gin.Context, logFormater *log.LogFormat, req *http.Request) {
ctx := c.Request.Context()
span := opentracing.SpanFromContext(ctx)
if span == nil {
panic("nil span")
}
//trace info 注入req.Header
span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
req.Header.Set("x-hop", logFormater.XHop.Hex())
}
req, err := http.NewRequest("GET", fmt.Sprintf("http://127.0.0.1:777/ping?x-logid=%s", logFormater.LogId), nil)
util.Must(err)
ctx := self.C.Request.Context()
span := opentracing.SpanFromContext(ctx)
if span == nil {
panic("nil span")
}
//trace info 注入req.Header
span.Tracer().Inject(span.Context(),opentracing.HTTPHeaders,opentracing.HTTPHeadersCarrier(req.Header))
req.Header.Set("x-hop", logFormater.XHop.Hex())
resp, err := http.DefaultClient.Do(req)
util.Must(err)
bs, err := ioutil.ReadAll(resp.Body)
util.Must(err)
fmt.Println(bs)