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 }