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