github.com/craicoverflow/tyk@v2.9.6-rc3+incompatible/trace/openzipkin/zipkin.go (about)

     1  package openzipkin
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/TykTechnologies/tyk/config"
    10  	opentracing "github.com/opentracing/opentracing-go"
    11  	"github.com/opentracing/opentracing-go/log"
    12  	zipkin "github.com/openzipkin/zipkin-go"
    13  	"github.com/openzipkin/zipkin-go/model"
    14  	"github.com/openzipkin/zipkin-go/propagation/b3"
    15  	"github.com/openzipkin/zipkin-go/reporter"
    16  	"github.com/openzipkin/zipkin-go/reporter/http"
    17  )
    18  
    19  var _ opentracing.Tracer = (*zipkinTracer)(nil)
    20  var _ opentracing.SpanContext = (*spanContext)(nil)
    21  var _ opentracing.Span = (*Span)(nil)
    22  
    23  const Name = "zipkin"
    24  
    25  type Span struct {
    26  	span zipkin.Span
    27  	tr   *zipkinTracer
    28  }
    29  
    30  func (s Span) Context() opentracing.SpanContext {
    31  	return spanContext{s.span.Context()}
    32  }
    33  
    34  func (s Span) Finish() {
    35  	s.span.Finish()
    36  }
    37  
    38  func (s Span) FinishWithOptions(opts opentracing.FinishOptions) {
    39  	s.span.Finish()
    40  }
    41  
    42  func (s Span) SetOperationName(operationName string) opentracing.Span {
    43  	s.span.SetName(operationName)
    44  	return s
    45  }
    46  
    47  func (s Span) SetTag(key string, value interface{}) opentracing.Span {
    48  	s.span.Tag(key, fmt.Sprint(value))
    49  	return s
    50  }
    51  
    52  func (s Span) LogFields(fields ...log.Field) {
    53  	now := time.Now()
    54  	lg := &logEncoder{h: func(key string, value interface{}) {
    55  		s.span.Annotate(now, fmt.Sprintf("%s %s", key, value))
    56  	}}
    57  	for _, field := range fields {
    58  		field.Marshal(lg)
    59  	}
    60  }
    61  
    62  type logEncoder struct {
    63  	h func(string, interface{})
    64  }
    65  
    66  func (e *logEncoder) emit(key string, value interface{}) {
    67  	if e.h != nil {
    68  		e.h(key, value)
    69  	}
    70  }
    71  func (e *logEncoder) EmitString(key, value string)             { e.emit(key, value) }
    72  func (e *logEncoder) EmitBool(key string, value bool)          { e.emit(key, value) }
    73  func (e *logEncoder) EmitInt(key string, value int)            { e.emit(key, value) }
    74  func (e *logEncoder) EmitInt32(key string, value int32)        { e.emit(key, value) }
    75  func (e *logEncoder) EmitInt64(key string, value int64)        { e.emit(key, value) }
    76  func (e *logEncoder) EmitUint32(key string, value uint32)      { e.emit(key, value) }
    77  func (e *logEncoder) EmitUint64(key string, value uint64)      { e.emit(key, value) }
    78  func (e *logEncoder) EmitFloat32(key string, value float32)    { e.emit(key, value) }
    79  func (e *logEncoder) EmitFloat64(key string, value float64)    { e.emit(key, value) }
    80  func (e *logEncoder) EmitObject(key string, value interface{}) { e.emit(key, value) }
    81  func (e *logEncoder) EmitLazyLogger(value log.LazyLogger)      {}
    82  
    83  func (s Span) LogKV(alternatingKeyValues ...interface{})                   {}
    84  func (s Span) SetBaggageItem(restrictedKey, value string) opentracing.Span { return s }
    85  func (Span) BaggageItem(restrictedKey string) string                       { return "" }
    86  func (s Span) Tracer() opentracing.Tracer                                  { return s.tr }
    87  func (s Span) LogEvent(event string)                                       {}
    88  func (s Span) LogEventWithPayload(event string, payload interface{})       {}
    89  func (s Span) Log(data opentracing.LogData)                                {}
    90  
    91  type spanContext struct {
    92  	model.SpanContext
    93  }
    94  
    95  func (spanContext) ForeachBaggageItem(handler func(k, v string) bool) {}
    96  
    97  type extractor interface {
    98  	extract(carrier interface{}) (spanContext, error)
    99  }
   100  
   101  var emptyContext spanContext
   102  
   103  func extractHTTPHeader(carrier interface{}) (spanContext, error) {
   104  	c, ok := carrier.(opentracing.HTTPHeadersCarrier)
   105  	if !ok {
   106  		return emptyContext, opentracing.ErrInvalidCarrier
   107  	}
   108  	var (
   109  		traceIDHeader      string
   110  		spanIDHeader       string
   111  		parentSpanIDHeader string
   112  		sampledHeader      string
   113  		flagsHeader        string
   114  		singleHeader       string
   115  	)
   116  	err := c.ForeachKey(func(key, val string) error {
   117  		switch strings.ToLower(key) {
   118  		case b3.TraceID:
   119  			traceIDHeader = val
   120  		case b3.SpanID:
   121  			spanIDHeader = val
   122  		case b3.ParentSpanID:
   123  			parentSpanIDHeader = val
   124  		case b3.Sampled:
   125  			sampledHeader = val
   126  		case b3.Flags:
   127  			flagsHeader = val
   128  		case b3.Context:
   129  			singleHeader = val
   130  		}
   131  		return nil
   132  	})
   133  	if err != nil {
   134  		return emptyContext, err
   135  	}
   136  	if singleHeader != "" {
   137  		ctx, err := b3.ParseSingleHeader(singleHeader)
   138  		if err != nil {
   139  			return emptyContext, err
   140  		}
   141  		return spanContext{*ctx}, nil
   142  	}
   143  	ctx, err := b3.ParseHeaders(
   144  		traceIDHeader, spanIDHeader, parentSpanIDHeader,
   145  		sampledHeader, flagsHeader,
   146  	)
   147  	if err != nil {
   148  		return emptyContext, err
   149  	}
   150  	return spanContext{*ctx}, nil
   151  }
   152  
   153  type extractorFn func(carrier interface{}) (spanContext, error)
   154  
   155  func (fn extractorFn) extract(carrier interface{}) (spanContext, error) {
   156  	return fn(carrier)
   157  }
   158  
   159  type injector interface {
   160  	inject(ctx spanContext, carrier interface{}) error
   161  }
   162  
   163  func injectHTTPHeaders(ctx spanContext, carrier interface{}) error {
   164  	c, ok := carrier.(opentracing.HTTPHeadersCarrier)
   165  	if !ok {
   166  		return opentracing.ErrInvalidCarrier
   167  	}
   168  	if ctx == emptyContext {
   169  		return nil
   170  	}
   171  	c.Set(b3.Context, b3.BuildSingleHeader(ctx.SpanContext))
   172  	return nil
   173  }
   174  
   175  type injectorFn func(ctx spanContext, carrier interface{}) error
   176  
   177  func (fn injectorFn) inject(ctx spanContext, carrier interface{}) error {
   178  	return fn(ctx, carrier)
   179  }
   180  
   181  type zipkinTracer struct {
   182  	zip        *zipkin.Tracer
   183  	extractors map[interface{}]extractor
   184  	injectors  map[interface{}]injector
   185  }
   186  
   187  func NewTracer(zip *zipkin.Tracer) *zipkinTracer {
   188  	return &zipkinTracer{
   189  		zip: zip,
   190  		extractors: map[interface{}]extractor{
   191  			opentracing.HTTPHeaders: extractorFn(extractHTTPHeader),
   192  		},
   193  		injectors: map[interface{}]injector{
   194  			opentracing.HTTPHeaders: injectorFn(injectHTTPHeaders),
   195  		},
   196  	}
   197  }
   198  
   199  func (z *zipkinTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
   200  	var o []zipkin.SpanOption
   201  	if len(opts) > 0 {
   202  		var os opentracing.StartSpanOptions
   203  		for _, opt := range opts {
   204  			opt.Apply(&os)
   205  		}
   206  		if len(os.Tags) > 0 {
   207  			t := make(map[string]string)
   208  			for k, v := range os.Tags {
   209  				t[k] = fmt.Sprint(v)
   210  			}
   211  			o = append(o, zipkin.Tags(t))
   212  		}
   213  		for _, ref := range os.References {
   214  			switch ref.Type {
   215  			case opentracing.ChildOfRef:
   216  				sp := ref.ReferencedContext.(spanContext)
   217  				o = append(o, zipkin.Parent(
   218  					sp.SpanContext,
   219  				))
   220  			}
   221  		}
   222  	}
   223  	sp := z.zip.StartSpan(operationName, o...)
   224  	return Span{tr: z, span: sp}
   225  }
   226  
   227  func (z *zipkinTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
   228  	if x, ok := z.extractors[format]; ok {
   229  		return x.extract(carrier)
   230  	}
   231  	return nil, opentracing.ErrUnsupportedFormat
   232  }
   233  
   234  func (z *zipkinTracer) Inject(ctx opentracing.SpanContext, format interface{}, carrier interface{}) error {
   235  	c, ok := ctx.(spanContext)
   236  	if !ok {
   237  		return opentracing.ErrInvalidSpanContext
   238  	}
   239  	if x, ok := z.injectors[format]; ok {
   240  		return x.inject(c, carrier)
   241  	}
   242  	return opentracing.ErrUnsupportedFormat
   243  }
   244  
   245  type Tracer struct {
   246  	opentracing.Tracer
   247  	reporter.Reporter
   248  }
   249  
   250  func (Tracer) Name() string {
   251  	return Name
   252  }
   253  
   254  func Init(service string, opts map[string]interface{}) (*Tracer, error) {
   255  	c, err := Load(opts)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  	if c.Reporter.URL == "" {
   260  		return nil, errors.New("zipkin: missing url")
   261  	}
   262  	r := http.NewReporter(c.Reporter.URL)
   263  	endpoint, err := zipkin.NewEndpoint(service, "")
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  	sampler, err := getSampler(c.Sampler)
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  	tr, err := zipkin.NewTracer(r,
   272  		zipkin.WithLocalEndpoint(endpoint),
   273  		zipkin.WithSampler(sampler),
   274  	)
   275  	if err != nil {
   276  		return nil, err
   277  	}
   278  	return &Tracer{Tracer: NewTracer(tr), Reporter: r}, nil
   279  }
   280  
   281  func getSampler(s config.Sampler) (zipkin.Sampler, error) {
   282  	if s.Name == "" {
   283  		return zipkin.AlwaysSample, nil
   284  	}
   285  	switch s.Name {
   286  	case "boundary":
   287  		return zipkin.NewBoundarySampler(s.Rate, s.Salt)
   288  	case "count":
   289  		return zipkin.NewCountingSampler(s.Rate)
   290  	case "mod":
   291  		return zipkin.NewModuloSampler(s.Mod), nil
   292  	}
   293  	return nil, fmt.Errorf("zipkin: unknown sampler %s", s.Name)
   294  }