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 }