github.com/anuvu/tyk@v2.9.0-beta9-dl-apic+incompatible/trace/manager.go (about) 1 package trace 2 3 import ( 4 "context" 5 "errors" 6 "net/http" 7 "strconv" 8 "sync" 9 "sync/atomic" 10 11 "github.com/TykTechnologies/tyk/request" 12 opentracing "github.com/opentracing/opentracing-go" 13 ) 14 15 var ErrManagerDisabled = errors.New("trace: trace is diabled") 16 17 // we use a global manager to avoid manual management as for our use case we 18 // only deal with on tracing server at a time. 19 var manager = NewManager(nil) 20 21 // serviceID key used to store the service name in request context.Context. 22 type serviceID = struct{} 23 24 // SetServiceID returns context with service assigned to it. 25 func SetServiceID(ctx context.Context, service string) context.Context { 26 return context.WithValue(ctx, serviceID{}, service) 27 } 28 29 // GetServiceID returns service name attched to context returns an empty string 30 // if the service name key is not found. 31 func GetServiceID(ctx context.Context) string { 32 if v := ctx.Value(serviceID{}); v != nil { 33 return v.(string) 34 } 35 return "" 36 } 37 38 // Logger defines api for logging messages by the OpenTracer struct. This is a 39 // workaround to avoid trying this to logrus 40 type Logger interface { 41 Errorf(format string, args ...interface{}) 42 Info(args ...interface{}) 43 Infof(format string, args ...interface{}) 44 } 45 46 // OpenTracer manages initializing,storage and retrieving on multiple tracers 47 // based on service names. 48 type OpenTracer struct { 49 mu sync.RWMutex 50 services map[string]Tracer 51 log Logger 52 enabled atomic.Value 53 config Config 54 } 55 56 type Config struct { 57 Name string 58 Opts map[string]interface{} 59 } 60 61 // NewManager returns a new opentrace manager. If log is not nil it will be used 62 // to log errors and info by the manager. 63 func NewManager(log Logger) *OpenTracer { 64 return &OpenTracer{log: log, services: make(map[string]Tracer)} 65 } 66 67 // Get returns a tracer for a given service, it returns a NoopTracer if there is 68 // no tracer for the service found. 69 func (o *OpenTracer) Get(service string) Tracer { 70 o.mu.RLock() 71 t, ok := o.services[service] 72 o.mu.RUnlock() 73 if !ok { 74 if o.log != nil { 75 o.log.Info(service, "not found") 76 } 77 return NoopTracer{} 78 } 79 return t 80 } 81 82 // Get returns a tracer stored on the global trace manager. 83 func Get(service string) Tracer { 84 return manager.Get(service) 85 } 86 87 // GetOk like Get but instead of returning NoopTracer for missing tracer it 88 // returns nil and false when the service tracer wasn't found. 89 func (o *OpenTracer) GetOk(service string) (Tracer, bool) { 90 o.mu.RLock() 91 t, ok := o.services[service] 92 o.mu.RUnlock() 93 return t, ok 94 } 95 96 // Set saves tr using service as key on o. 97 func (o *OpenTracer) Set(service string, tr Tracer) { 98 o.mu.Lock() 99 o.services[service] = tr 100 o.mu.Unlock() 101 } 102 103 // Close calls Close on the active tracer. 104 func (o *OpenTracer) Close() error { 105 o.mu.RLock() 106 for _, v := range o.services { 107 if err := v.Close(); err != nil { 108 return err 109 } 110 } 111 o.mu.RUnlock() 112 o.mu.Lock() 113 o.services = make(map[string]Tracer) 114 o.mu.Unlock() 115 return nil 116 } 117 118 // Close calls Close on the global tace manager. 119 func Close() error { 120 return manager.Close() 121 } 122 123 // IsEnabled returns true if the manager is enabled. 124 func (o *OpenTracer) IsEnabled() bool { 125 ok := o.enabled.Load() 126 if ok != nil { 127 return ok.(bool) 128 } 129 return false 130 } 131 132 // IsEnabled returns true if the global trace manager is enabled. 133 func IsEnabled() bool { 134 return manager.IsEnabled() 135 } 136 137 // Enable sets o to enabled state. 138 func (o *OpenTracer) Enable() { 139 o.enabled.Store(true) 140 } 141 142 // Enable sets the global manager to enabled. 143 func Enable() { 144 manager.Enable() 145 } 146 147 // Disable sets o to disabled state. 148 func (o *OpenTracer) Disable() { 149 o.enabled.Store(false) 150 } 151 152 // Disable disables the global trace manager. 153 func Disable() { 154 manager.Disable() 155 } 156 157 // SetLogger sets log as the default logger for o. 158 func (o *OpenTracer) SetLogger(log Logger) { 159 o.mu.Lock() 160 o.log = log 161 o.mu.Unlock() 162 } 163 164 // AddTracer initializes a tracer based on the configuration stored in o for the 165 // given service name and caches. This does donthing when there is already a 166 // tracer for the given service. 167 func (o *OpenTracer) AddTracer(service string) error { 168 _, ok := o.GetOk(service) 169 if !ok { 170 tr, err := Init(o.config.Name, service, o.config.Opts, o.log) 171 if err != nil { 172 if o.log != nil { 173 o.log.Errorf("%v", err) 174 } 175 return err 176 } 177 o.Set(service, tr) 178 } 179 return nil 180 } 181 182 // AddTracer initialize a tracer for the service. 183 func AddTracer(service string) error { 184 if !manager.IsEnabled() { 185 return ErrManagerDisabled 186 } 187 return manager.AddTracer(service) 188 } 189 190 func SetLogger(log Logger) { 191 manager.SetLogger(log) 192 } 193 194 func (o *OpenTracer) SetupTracing(name string, opts map[string]interface{}) { 195 o.config.Name = name 196 o.config.Opts = opts 197 } 198 199 func SetupTracing(name string, opts map[string]interface{}) { 200 manager.SetupTracing(name, opts) 201 manager.Enable() 202 } 203 204 func Root(service string, r *http.Request) (opentracing.Span, *http.Request) { 205 tr := Get(service) 206 mainCtx, err := Extract(tr, r.Header) 207 tags := opentracing.Tags{ 208 "from_ip": request.RealIP(r), 209 "method": r.Method, 210 "endpoint": r.URL.Path, 211 "raw_url": r.URL.String(), 212 "size": strconv.Itoa(int(r.ContentLength)), 213 } 214 if err != nil { 215 // TODO log this error? 216 // We just create a new span here so the log should be a warning. 217 span, ctx := opentracing.StartSpanFromContextWithTracer(r.Context(), 218 tr, 219 service, tags) 220 return span, r.WithContext(SetServiceID(ctx, service)) 221 } 222 span, ctx := opentracing.StartSpanFromContextWithTracer(r.Context(), 223 tr, 224 service, 225 opentracing.ChildOf(mainCtx), tags) 226 return span, r.WithContext(SetServiceID(ctx, service)) 227 } 228 229 // Span creates a new span for the given ops. If tracing is disabled in this ctx 230 // then a noop span is created and the same ctx is returned. 231 // 232 // Note that the returned context contains the returned span as active span. So 233 // any spans created form the returned context will be children of the returned 234 // span. 235 func Span(ctx context.Context, ops string, opts ...opentracing.StartSpanOption) (opentracing.Span, context.Context) { 236 return opentracing.StartSpanFromContextWithTracer(ctx, 237 Get(GetServiceID(ctx)), 238 ops, opts...) 239 } 240 241 func Extract(tr Tracer, h http.Header) (opentracing.SpanContext, error) { 242 return tr.Extract( 243 opentracing.HTTPHeaders, 244 opentracing.HTTPHeadersCarrier(h), 245 ) 246 } 247 248 func ExtractFromContext(ctx context.Context, h http.Header) (opentracing.SpanContext, error) { 249 return Extract(Get(GetServiceID(ctx)), h) 250 } 251 252 func Inject(service string, span opentracing.Span, h http.Header) error { 253 tr := Get(service) 254 return tr.Inject( 255 span.Context(), 256 opentracing.HTTPHeaders, 257 opentracing.HTTPHeadersCarrier(h), 258 ) 259 } 260 261 func InjectFromContext(ctx context.Context, span opentracing.Span, h http.Header) error { 262 return Inject(GetServiceID(ctx), span, h) 263 }