1// Copyright The OpenTelemetry Authors
2// SPDX-License-Identifier: Apache-2.0
3
4package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
5
6import (
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.
20const 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.
24type 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.
44type Option interface {
45 apply(*config)
46}
47
48type optionFunc func(*config)
49
50func (o optionFunc) apply(c *config) {
51 o(c)
52}
53
54// newConfig creates a new config struct and applies opts to it.
55func 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.
79func 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.
89func 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.
100func 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.
111func 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.
119func 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.
129func 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.
141func WithFilter(f Filter) Option {
142 return optionFunc(func(c *config) {
143 c.Filters = append(c.Filters, f)
144 })
145}
146
147type event int
148
149// Different types of events that can be recorded, see WithMessageEvents.
150const (
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
164func 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.
179func WithSpanNameFormatter(f func(operation string, r *http.Request) string) Option {
180 return optionFunc(func(c *config) {
181 c.SpanNameFormatter = f
182 })
183}
184
185// WithClientTrace takes a function that returns client trace instance that will be
186// applied to the requests sent through the otelhttp Transport.
187func WithClientTrace(f func(context.Context) *httptrace.ClientTrace) Option {
188 return optionFunc(func(c *config) {
189 c.ClientTrace = f
190 })
191}
192
193// WithServerName returns an Option that sets the name of the (virtual) server
194// handling requests.
195func WithServerName(server string) Option {
196 return optionFunc(func(c *config) {
197 c.ServerName = server
198 })
199}
200
201// WithMetricAttributesFn returns an Option to set a function that maps an HTTP request to a slice of attribute.KeyValue.
202// These attributes will be included in metrics for every request.
203func WithMetricAttributesFn(metricAttributesFn func(r *http.Request) []attribute.KeyValue) Option {
204 return optionFunc(func(c *config) {
205 c.MetricAttributesFn = metricAttributesFn
206 })
207}