config.go raw

   1  // Copyright The OpenTelemetry Authors
   2  // SPDX-License-Identifier: Apache-2.0
   3  
   4  package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
   5  
   6  import (
   7  	"context"
   8  	"net/http"
   9  	"net/http/httptrace"
  10  
  11  	"go.opentelemetry.io/otel/attribute"
  12  
  13  	"go.opentelemetry.io/otel"
  14  	"go.opentelemetry.io/otel/metric"
  15  	"go.opentelemetry.io/otel/propagation"
  16  	"go.opentelemetry.io/otel/trace"
  17  )
  18  
  19  // ScopeName is the instrumentation scope name.
  20  const ScopeName = "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
  21  
  22  // config represents the configuration options available for the http.Handler
  23  // and http.Transport types.
  24  type config struct {
  25  	ServerName        string
  26  	Tracer            trace.Tracer
  27  	Meter             metric.Meter
  28  	Propagators       propagation.TextMapPropagator
  29  	SpanStartOptions  []trace.SpanStartOption
  30  	PublicEndpoint    bool
  31  	PublicEndpointFn  func(*http.Request) bool
  32  	ReadEvent         bool
  33  	WriteEvent        bool
  34  	Filters           []Filter
  35  	SpanNameFormatter func(string, *http.Request) string
  36  	ClientTrace       func(context.Context) *httptrace.ClientTrace
  37  
  38  	TracerProvider     trace.TracerProvider
  39  	MeterProvider      metric.MeterProvider
  40  	MetricAttributesFn func(*http.Request) []attribute.KeyValue
  41  }
  42  
  43  // Option interface used for setting optional config properties.
  44  type Option interface {
  45  	apply(*config)
  46  }
  47  
  48  type optionFunc func(*config)
  49  
  50  func (o optionFunc) apply(c *config) {
  51  	o(c)
  52  }
  53  
  54  // newConfig creates a new config struct and applies opts to it.
  55  func newConfig(opts ...Option) *config {
  56  	c := &config{
  57  		Propagators:   otel.GetTextMapPropagator(),
  58  		MeterProvider: otel.GetMeterProvider(),
  59  	}
  60  	for _, opt := range opts {
  61  		opt.apply(c)
  62  	}
  63  
  64  	// Tracer is only initialized if manually specified. Otherwise, can be passed with the tracing context.
  65  	if c.TracerProvider != nil {
  66  		c.Tracer = newTracer(c.TracerProvider)
  67  	}
  68  
  69  	c.Meter = c.MeterProvider.Meter(
  70  		ScopeName,
  71  		metric.WithInstrumentationVersion(Version()),
  72  	)
  73  
  74  	return c
  75  }
  76  
  77  // WithTracerProvider specifies a tracer provider to use for creating a tracer.
  78  // If none is specified, the global provider is used.
  79  func WithTracerProvider(provider trace.TracerProvider) Option {
  80  	return optionFunc(func(cfg *config) {
  81  		if provider != nil {
  82  			cfg.TracerProvider = provider
  83  		}
  84  	})
  85  }
  86  
  87  // WithMeterProvider specifies a meter provider to use for creating a meter.
  88  // If none is specified, the global provider is used.
  89  func WithMeterProvider(provider metric.MeterProvider) Option {
  90  	return optionFunc(func(cfg *config) {
  91  		if provider != nil {
  92  			cfg.MeterProvider = provider
  93  		}
  94  	})
  95  }
  96  
  97  // WithPublicEndpoint configures the Handler to link the span with an incoming
  98  // span context. If this option is not provided, then the association is a child
  99  // association instead of a link.
 100  func WithPublicEndpoint() Option {
 101  	return optionFunc(func(c *config) {
 102  		c.PublicEndpoint = true
 103  	})
 104  }
 105  
 106  // WithPublicEndpointFn runs with every request, and allows conditionally
 107  // configuring the Handler to link the span with an incoming span context. If
 108  // this option is not provided or returns false, then the association is a
 109  // child association instead of a link.
 110  // Note: WithPublicEndpoint takes precedence over WithPublicEndpointFn.
 111  func WithPublicEndpointFn(fn func(*http.Request) bool) Option {
 112  	return optionFunc(func(c *config) {
 113  		c.PublicEndpointFn = fn
 114  	})
 115  }
 116  
 117  // WithPropagators configures specific propagators. If this
 118  // option isn't specified, then the global TextMapPropagator is used.
 119  func WithPropagators(ps propagation.TextMapPropagator) Option {
 120  	return optionFunc(func(c *config) {
 121  		if ps != nil {
 122  			c.Propagators = ps
 123  		}
 124  	})
 125  }
 126  
 127  // WithSpanOptions configures an additional set of
 128  // trace.SpanOptions, which are applied to each new span.
 129  func WithSpanOptions(opts ...trace.SpanStartOption) Option {
 130  	return optionFunc(func(c *config) {
 131  		c.SpanStartOptions = append(c.SpanStartOptions, opts...)
 132  	})
 133  }
 134  
 135  // WithFilter adds a filter to the list of filters used by the handler.
 136  // If any filter indicates to exclude a request then the request will not be
 137  // traced. All filters must allow a request to be traced for a Span to be created.
 138  // If no filters are provided then all requests are traced.
 139  // Filters will be invoked for each processed request, it is advised to make them
 140  // simple and fast.
 141  func WithFilter(f Filter) Option {
 142  	return optionFunc(func(c *config) {
 143  		c.Filters = append(c.Filters, f)
 144  	})
 145  }
 146  
 147  type event int
 148  
 149  // Different types of events that can be recorded, see WithMessageEvents.
 150  const (
 151  	ReadEvents event = iota
 152  	WriteEvents
 153  )
 154  
 155  // WithMessageEvents configures the Handler to record the specified events
 156  // (span.AddEvent) on spans. By default only summary attributes are added at the
 157  // end of the request.
 158  //
 159  // Valid events are:
 160  //   - ReadEvents: Record the number of bytes read after every http.Request.Body.Read
 161  //     using the ReadBytesKey
 162  //   - WriteEvents: Record the number of bytes written after every http.ResponeWriter.Write
 163  //     using the WriteBytesKey
 164  func WithMessageEvents(events ...event) Option {
 165  	return optionFunc(func(c *config) {
 166  		for _, e := range events {
 167  			switch e {
 168  			case ReadEvents:
 169  				c.ReadEvent = true
 170  			case WriteEvents:
 171  				c.WriteEvent = true
 172  			}
 173  		}
 174  	})
 175  }
 176  
 177  // WithSpanNameFormatter takes a function that will be called on every
 178  // request and the returned string will become the Span Name.
 179  //
 180  // When using [http.ServeMux] (or any middleware that sets the Pattern of [http.Request]),
 181  // the span name formatter will run twice. Once when the span is created, and
 182  // second time after the middleware, so the pattern can be used.
 183  func WithSpanNameFormatter(f func(operation string, r *http.Request) string) Option {
 184  	return optionFunc(func(c *config) {
 185  		c.SpanNameFormatter = f
 186  	})
 187  }
 188  
 189  // WithClientTrace takes a function that returns client trace instance that will be
 190  // applied to the requests sent through the otelhttp Transport.
 191  func WithClientTrace(f func(context.Context) *httptrace.ClientTrace) Option {
 192  	return optionFunc(func(c *config) {
 193  		c.ClientTrace = f
 194  	})
 195  }
 196  
 197  // WithServerName returns an Option that sets the name of the (virtual) server
 198  // handling requests.
 199  func WithServerName(server string) Option {
 200  	return optionFunc(func(c *config) {
 201  		c.ServerName = server
 202  	})
 203  }
 204  
 205  // WithMetricAttributesFn returns an Option to set a function that maps an HTTP request to a slice of attribute.KeyValue.
 206  // These attributes will be included in metrics for every request.
 207  func WithMetricAttributesFn(metricAttributesFn func(r *http.Request) []attribute.KeyValue) Option {
 208  	return optionFunc(func(c *config) {
 209  		c.MetricAttributesFn = metricAttributesFn
 210  	})
 211  }
 212