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  }