github.com/instana/go-sensor@v1.62.2-0.20240520081010-4919868049e1/adapters.go (about)

     1  // (c) Copyright IBM Corp. 2021
     2  // (c) Copyright Instana Inc. 2019
     3  
     4  package instana
     5  
     6  import (
     7  	"context"
     8  	"net/http"
     9  	"runtime"
    10  
    11  	"github.com/opentracing/opentracing-go"
    12  	ot "github.com/opentracing/opentracing-go"
    13  	"github.com/opentracing/opentracing-go/ext"
    14  	otlog "github.com/opentracing/opentracing-go/log"
    15  )
    16  
    17  var _ TracerLogger = (*Sensor)(nil)
    18  
    19  type SensorLogger interface {
    20  	Tracer() ot.Tracer
    21  	Logger() LeveledLogger
    22  	SetLogger(l LeveledLogger)
    23  }
    24  
    25  // SpanSensitiveFunc is a function executed within a span context
    26  //
    27  // Deprecated: use instana.ContextWithSpan() and instana.SpanFromContext() to inject and retrieve spans
    28  type SpanSensitiveFunc func(span ot.Span)
    29  
    30  // ContextSensitiveFunc is a SpanSensitiveFunc that also takes context.Context
    31  //
    32  // Deprecated: use instana.ContextWithSpan() and instana.SpanFromContext() to inject and retrieve spans
    33  type ContextSensitiveFunc func(span ot.Span, ctx context.Context)
    34  
    35  // Tracer extends the opentracing.Tracer interface
    36  type Tracer interface {
    37  	opentracing.Tracer
    38  
    39  	// Options gets the current tracer options
    40  	Options() TracerOptions
    41  	// Flush sends all finished spans to the agent
    42  	Flush(context.Context) error
    43  	// StartSpanWithOptions starts a span with the given options and return the span reference
    44  	StartSpanWithOptions(string, ot.StartSpanOptions) ot.Span
    45  }
    46  
    47  // Sensor is used to inject tracing information into requests
    48  type Sensor struct {
    49  	tracer ot.Tracer
    50  	logger LeveledLogger
    51  }
    52  
    53  // NewSensor creates a new [Sensor]
    54  func NewSensor(serviceName string) *Sensor {
    55  	return NewSensorWithTracer(NewTracerWithOptions(
    56  		&Options{
    57  			Service: serviceName,
    58  		},
    59  	))
    60  }
    61  
    62  // NewSensorWithTracer returns a new [Sensor] that uses provided tracer to report spans
    63  func NewSensorWithTracer(tracer ot.Tracer) *Sensor {
    64  	return &Sensor{
    65  		tracer: tracer,
    66  		logger: defaultLogger,
    67  	}
    68  }
    69  
    70  // Tracer returns the tracer instance for this sensor
    71  func (s *Sensor) Tracer() ot.Tracer {
    72  	return s.tracer
    73  }
    74  
    75  // Logger returns the logger instance for this sensor
    76  func (s *Sensor) Logger() LeveledLogger {
    77  	return s.logger
    78  }
    79  
    80  // SetLogger sets the logger for this sensor
    81  func (s *Sensor) SetLogger(l LeveledLogger) {
    82  	s.logger = l
    83  }
    84  
    85  // TraceHandler is similar to TracingHandler in regards, that it wraps an existing http.HandlerFunc
    86  // into a named instance to support capturing tracing information and data. The returned values are
    87  // compatible with handler registration methods, e.g. http.Handle()
    88  //
    89  // Deprecated: please use instana.TracingHandlerFunc() instead
    90  func (s *Sensor) TraceHandler(name, pattern string, handler http.HandlerFunc) (string, http.HandlerFunc) {
    91  	return pattern, s.TracingHandler(name, handler)
    92  }
    93  
    94  // TracingHandler wraps an existing http.HandlerFunc into a named instance to support capturing tracing
    95  // information and response data
    96  //
    97  // Deprecated: please use instana.TracingHandlerFunc() instead
    98  func (s *Sensor) TracingHandler(name string, handler http.HandlerFunc) http.HandlerFunc {
    99  	return TracingHandlerFunc(s, name, handler)
   100  }
   101  
   102  // TracingHttpRequest wraps an existing http.Request instance into a named instance to inject tracing and span
   103  // header information into the actual HTTP wire transfer
   104  //
   105  // Deprecated: please use instana.RoundTripper() instead
   106  func (s *Sensor) TracingHttpRequest(name string, parent, req *http.Request, client http.Client) (*http.Response, error) {
   107  	client.Transport = RoundTripper(s, client.Transport)
   108  	return client.Do(req.WithContext(context.Background()))
   109  }
   110  
   111  // WithTracingSpan takes the given SpanSensitiveFunc and executes it under the scope of a child span, which is
   112  // injected as an argument when calling the function. It uses the name of the caller as a span operation name
   113  // unless a non-empty value is provided
   114  //
   115  // Deprecated: please use instana.TracingHandlerFunc() to instrument an HTTP handler
   116  func (s *Sensor) WithTracingSpan(operationName string, w http.ResponseWriter, req *http.Request, f SpanSensitiveFunc) {
   117  	if operationName == "" {
   118  		pc, _, _, _ := runtime.Caller(1)
   119  		f := runtime.FuncForPC(pc)
   120  		operationName = f.Name()
   121  	}
   122  
   123  	opts := []ot.StartSpanOption{
   124  		ext.SpanKindRPCServer,
   125  
   126  		ot.Tags{
   127  			string(ext.PeerHostname): req.Host,
   128  			string(ext.HTTPUrl):      req.URL.Path,
   129  			string(ext.HTTPMethod):   req.Method,
   130  		},
   131  	}
   132  
   133  	wireContext, err := s.tracer.Extract(ot.HTTPHeaders, ot.HTTPHeadersCarrier(req.Header))
   134  	switch err {
   135  	case nil:
   136  		opts = append(opts, ext.RPCServerOption(wireContext))
   137  	case ot.ErrSpanContextNotFound:
   138  		s.Logger().Debug("no span context provided with ", req.Method, " ", req.URL.Path)
   139  	case ot.ErrUnsupportedFormat:
   140  		s.Logger().Info("unsupported span context format provided with ", req.Method, " ", req.URL.Path)
   141  	default:
   142  		s.Logger().Warn("failed to extract span context from the request:", err)
   143  	}
   144  
   145  	if ps, ok := SpanFromContext(req.Context()); ok {
   146  		opts = append(opts, ot.ChildOf(ps.Context()))
   147  	}
   148  
   149  	span := s.tracer.StartSpan(operationName, opts...)
   150  	defer span.Finish()
   151  
   152  	defer func() {
   153  		// Capture outgoing headers
   154  		s.tracer.Inject(span.Context(), ot.HTTPHeaders, ot.HTTPHeadersCarrier(w.Header()))
   155  
   156  		// Be sure to capture any kind of panic / error
   157  		if err := recover(); err != nil {
   158  			if e, ok := err.(error); ok {
   159  				span.LogFields(otlog.Error(e))
   160  			} else {
   161  				span.LogFields(otlog.Object("error", err))
   162  			}
   163  
   164  			// re-throw the panic
   165  			panic(err)
   166  		}
   167  	}()
   168  
   169  	f(span)
   170  }
   171  
   172  // WithTracingContext executes the given ContextSensitiveFunc and executes it under the scope of a newly created context.Context,
   173  // that provides access to the parent span as 'parentSpan'.
   174  //
   175  // Deprecated: please use instana.TracingHandlerFunc() to instrument an HTTP handler
   176  func (s *Sensor) WithTracingContext(name string, w http.ResponseWriter, req *http.Request, f ContextSensitiveFunc) {
   177  	s.WithTracingSpan(name, w, req, func(span ot.Span) {
   178  		f(span, ContextWithSpan(req.Context(), span))
   179  	})
   180  }
   181  
   182  // Compliance with TracerLogger
   183  
   184  // Extract() returns a SpanContext instance given `format` and `carrier`. It matches [opentracing.Tracer.Extract].
   185  func (s *Sensor) Extract(format interface{}, carrier interface{}) (ot.SpanContext, error) {
   186  	return s.tracer.Extract(format, carrier)
   187  }
   188  
   189  // Inject() takes the `sm` SpanContext instance and injects it for
   190  // propagation within `carrier`. The actual type of `carrier` depends on
   191  // the value of `format`. It matches [opentracing.Tracer.Inject]
   192  func (s *Sensor) Inject(sm ot.SpanContext, format interface{}, carrier interface{}) error {
   193  	return s.tracer.Inject(sm, format, carrier)
   194  }
   195  
   196  // Create, start, and return a new Span with the given `operationName` and
   197  // incorporate the given StartSpanOption `opts`. (Note that `opts` borrows
   198  // from the "functional options" pattern, per
   199  // http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)
   200  //
   201  // It matches [opentracing.Tracer.StartSpan].
   202  func (s *Sensor) StartSpan(operationName string, opts ...ot.StartSpanOption) ot.Span {
   203  	return s.tracer.StartSpan(operationName, opts...)
   204  }
   205  
   206  // StartSpanWithOptions creates and starts a span by setting Instana relevant data within the span.
   207  // It matches [instana.Tracer.StartSpanWithOptions].
   208  func (s *Sensor) StartSpanWithOptions(operationName string, opts ot.StartSpanOptions) ot.Span {
   209  	if t, ok := s.tracer.(Tracer); ok {
   210  		return t.StartSpanWithOptions(operationName, opts)
   211  	}
   212  
   213  	s.logger.Warn("Sensor.StartSpanWithOptions() not implemented by interface: ", s.tracer, " - returning nil")
   214  
   215  	return nil
   216  }
   217  
   218  // Options gets the current tracer options
   219  // It matches [instana.Tracer.Options].
   220  func (s *Sensor) Options() TracerOptions {
   221  	if t, ok := s.tracer.(Tracer); ok {
   222  		return t.Options()
   223  	}
   224  
   225  	s.logger.Warn("Sensor.Options() not implemented by interface: ", s.tracer, " - returning DefaultTracerOptions()")
   226  
   227  	return DefaultTracerOptions()
   228  }
   229  
   230  // Flush sends all finished spans to the agent
   231  // It matches [instana.Tracer.Flush].
   232  func (s *Sensor) Flush(ctx context.Context) error {
   233  	if t, ok := s.tracer.(Tracer); ok {
   234  		return t.Flush(ctx)
   235  	}
   236  
   237  	s.logger.Warn("Sensor.Flush() not implemented by interface: ", s.tracer, " - returning nil")
   238  
   239  	return nil
   240  }
   241  
   242  // Debug logs a debug message by calling [LeveledLogger] underneath
   243  func (s *Sensor) Debug(v ...interface{}) {
   244  	s.logger.Debug(v...)
   245  }
   246  
   247  // Info logs an info message by calling [LeveledLogger] underneath
   248  func (s *Sensor) Info(v ...interface{}) {
   249  	s.logger.Info(v...)
   250  }
   251  
   252  // Warn logs a warning message by calling [LeveledLogger] underneath
   253  func (s *Sensor) Warn(v ...interface{}) {
   254  	s.logger.Warn(v...)
   255  }
   256  
   257  // Error logs a error message by calling [LeveledLogger] underneath
   258  func (s *Sensor) Error(v ...interface{}) {
   259  	s.logger.Error(v...)
   260  }
   261  
   262  // LegacySensor returns a reference to [Sensor].
   263  func (s *Sensor) LegacySensor() *Sensor {
   264  	return s
   265  }