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