env.go raw
1 // Code generated by gotmpl. DO NOT MODIFY.
2 // source: internal/shared/semconv/env.go.tmpl
3
4 // Copyright The OpenTelemetry Authors
5 // SPDX-License-Identifier: Apache-2.0
6
7 package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
8
9 import (
10 "context"
11 "fmt"
12 "net/http"
13 "os"
14 "strings"
15 "sync"
16
17 "go.opentelemetry.io/otel/attribute"
18 "go.opentelemetry.io/otel/codes"
19 "go.opentelemetry.io/otel/metric"
20 )
21
22 // OTelSemConvStabilityOptIn is an environment variable.
23 // That can be set to "http/dup" to keep getting the old HTTP semantic conventions.
24 const OTelSemConvStabilityOptIn = "OTEL_SEMCONV_STABILITY_OPT_IN"
25
26 type ResponseTelemetry struct {
27 StatusCode int
28 ReadBytes int64
29 ReadError error
30 WriteBytes int64
31 WriteError error
32 }
33
34 type HTTPServer struct {
35 duplicate bool
36
37 // Old metrics
38 requestBytesCounter metric.Int64Counter
39 responseBytesCounter metric.Int64Counter
40 serverLatencyMeasure metric.Float64Histogram
41
42 // New metrics
43 requestBodySizeHistogram metric.Int64Histogram
44 responseBodySizeHistogram metric.Int64Histogram
45 requestDurationHistogram metric.Float64Histogram
46 }
47
48 // RequestTraceAttrs returns trace attributes for an HTTP request received by a
49 // server.
50 //
51 // The server must be the primary server name if it is known. For example this
52 // would be the ServerName directive
53 // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
54 // server, and the server_name directive
55 // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
56 // nginx server. More generically, the primary server name would be the host
57 // header value that matches the default virtual host of an HTTP server. It
58 // should include the host identifier and if a port is used to route to the
59 // server that port identifier should be included as an appropriate port
60 // suffix.
61 //
62 // If the primary server name is not known, server should be an empty string.
63 // The req Host will be used to determine the server instead.
64 func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
65 attrs := CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts)
66 if s.duplicate {
67 return OldHTTPServer{}.RequestTraceAttrs(server, req, attrs)
68 }
69 return attrs
70 }
71
72 func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue {
73 if s.duplicate {
74 return []attribute.KeyValue{
75 OldHTTPServer{}.NetworkTransportAttr(network),
76 CurrentHTTPServer{}.NetworkTransportAttr(network),
77 }
78 }
79 return []attribute.KeyValue{
80 CurrentHTTPServer{}.NetworkTransportAttr(network),
81 }
82 }
83
84 // ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response.
85 //
86 // If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
87 func (s HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
88 attrs := CurrentHTTPServer{}.ResponseTraceAttrs(resp)
89 if s.duplicate {
90 return OldHTTPServer{}.ResponseTraceAttrs(resp, attrs)
91 }
92 return attrs
93 }
94
95 // Route returns the attribute for the route.
96 func (s HTTPServer) Route(route string) attribute.KeyValue {
97 return CurrentHTTPServer{}.Route(route)
98 }
99
100 // Status returns a span status code and message for an HTTP status code
101 // value returned by a server. Status codes in the 400-499 range are not
102 // returned as errors.
103 func (s HTTPServer) Status(code int) (codes.Code, string) {
104 if code < 100 || code >= 600 {
105 return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
106 }
107 if code >= 500 {
108 return codes.Error, ""
109 }
110 return codes.Unset, ""
111 }
112
113 type ServerMetricData struct {
114 ServerName string
115 ResponseSize int64
116
117 MetricData
118 MetricAttributes
119 }
120
121 type MetricAttributes struct {
122 Req *http.Request
123 StatusCode int
124 AdditionalAttributes []attribute.KeyValue
125 }
126
127 type MetricData struct {
128 RequestSize int64
129
130 // The request duration, in milliseconds
131 ElapsedTime float64
132 }
133
134 var (
135 metricAddOptionPool = &sync.Pool{
136 New: func() interface{} {
137 return &[]metric.AddOption{}
138 },
139 }
140
141 metricRecordOptionPool = &sync.Pool{
142 New: func() interface{} {
143 return &[]metric.RecordOption{}
144 },
145 }
146 )
147
148 func (s HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) {
149 if s.requestDurationHistogram != nil && s.requestBodySizeHistogram != nil && s.responseBodySizeHistogram != nil {
150 attributes := CurrentHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
151 o := metric.WithAttributeSet(attribute.NewSet(attributes...))
152 recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
153 *recordOpts = append(*recordOpts, o)
154 s.requestBodySizeHistogram.Record(ctx, md.RequestSize, *recordOpts...)
155 s.responseBodySizeHistogram.Record(ctx, md.ResponseSize, *recordOpts...)
156 s.requestDurationHistogram.Record(ctx, md.ElapsedTime/1000.0, o)
157 *recordOpts = (*recordOpts)[:0]
158 metricRecordOptionPool.Put(recordOpts)
159 }
160
161 if s.duplicate && s.requestBytesCounter != nil && s.responseBytesCounter != nil && s.serverLatencyMeasure != nil {
162 attributes := OldHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
163 o := metric.WithAttributeSet(attribute.NewSet(attributes...))
164 addOpts := metricAddOptionPool.Get().(*[]metric.AddOption)
165 *addOpts = append(*addOpts, o)
166 s.requestBytesCounter.Add(ctx, md.RequestSize, *addOpts...)
167 s.responseBytesCounter.Add(ctx, md.ResponseSize, *addOpts...)
168 s.serverLatencyMeasure.Record(ctx, md.ElapsedTime, o)
169 *addOpts = (*addOpts)[:0]
170 metricAddOptionPool.Put(addOpts)
171 }
172 }
173
174 // hasOptIn returns true if the comma-separated version string contains the
175 // exact optIn value.
176 func hasOptIn(version, optIn string) bool {
177 for _, v := range strings.Split(version, ",") {
178 if strings.TrimSpace(v) == optIn {
179 return true
180 }
181 }
182 return false
183 }
184
185 func NewHTTPServer(meter metric.Meter) HTTPServer {
186 env := strings.ToLower(os.Getenv(OTelSemConvStabilityOptIn))
187 duplicate := hasOptIn(env, "http/dup")
188 server := HTTPServer{
189 duplicate: duplicate,
190 }
191 server.requestBodySizeHistogram, server.responseBodySizeHistogram, server.requestDurationHistogram = CurrentHTTPServer{}.createMeasures(meter)
192 if duplicate {
193 server.requestBytesCounter, server.responseBytesCounter, server.serverLatencyMeasure = OldHTTPServer{}.createMeasures(meter)
194 }
195 return server
196 }
197
198 type HTTPClient struct {
199 duplicate bool
200
201 // old metrics
202 requestBytesCounter metric.Int64Counter
203 responseBytesCounter metric.Int64Counter
204 latencyMeasure metric.Float64Histogram
205
206 // new metrics
207 requestBodySize metric.Int64Histogram
208 requestDuration metric.Float64Histogram
209 }
210
211 func NewHTTPClient(meter metric.Meter) HTTPClient {
212 env := strings.ToLower(os.Getenv(OTelSemConvStabilityOptIn))
213 duplicate := hasOptIn(env, "http/dup")
214 client := HTTPClient{
215 duplicate: duplicate,
216 }
217 client.requestBodySize, client.requestDuration = CurrentHTTPClient{}.createMeasures(meter)
218 if duplicate {
219 client.requestBytesCounter, client.responseBytesCounter, client.latencyMeasure = OldHTTPClient{}.createMeasures(meter)
220 }
221
222 return client
223 }
224
225 // RequestTraceAttrs returns attributes for an HTTP request made by a client.
226 func (c HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
227 attrs := CurrentHTTPClient{}.RequestTraceAttrs(req)
228 if c.duplicate {
229 return OldHTTPClient{}.RequestTraceAttrs(req, attrs)
230 }
231 return attrs
232 }
233
234 // ResponseTraceAttrs returns metric attributes for an HTTP request made by a client.
235 func (c HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
236 attrs := CurrentHTTPClient{}.ResponseTraceAttrs(resp)
237 if c.duplicate {
238 return OldHTTPClient{}.ResponseTraceAttrs(resp, attrs)
239 }
240 return attrs
241 }
242
243 func (c HTTPClient) Status(code int) (codes.Code, string) {
244 if code < 100 || code >= 600 {
245 return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
246 }
247 if code >= 400 {
248 return codes.Error, ""
249 }
250 return codes.Unset, ""
251 }
252
253 func (c HTTPClient) ErrorType(err error) attribute.KeyValue {
254 return CurrentHTTPClient{}.ErrorType(err)
255 }
256
257 type MetricOpts struct {
258 measurement metric.MeasurementOption
259 addOptions metric.AddOption
260 }
261
262 func (o MetricOpts) MeasurementOption() metric.MeasurementOption {
263 return o.measurement
264 }
265
266 func (o MetricOpts) AddOptions() metric.AddOption {
267 return o.addOptions
268 }
269
270 func (c HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts {
271 opts := map[string]MetricOpts{}
272
273 attributes := CurrentHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
274 set := metric.WithAttributeSet(attribute.NewSet(attributes...))
275 opts["new"] = MetricOpts{
276 measurement: set,
277 addOptions: set,
278 }
279
280 if c.duplicate {
281 attributes := OldHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
282 set := metric.WithAttributeSet(attribute.NewSet(attributes...))
283 opts["old"] = MetricOpts{
284 measurement: set,
285 addOptions: set,
286 }
287 }
288
289 return opts
290 }
291
292 func (s HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts map[string]MetricOpts) {
293 if s.requestBodySize == nil || s.requestDuration == nil {
294 // This will happen if an HTTPClient{} is used instead of NewHTTPClient().
295 return
296 }
297
298 s.requestBodySize.Record(ctx, md.RequestSize, opts["new"].MeasurementOption())
299 s.requestDuration.Record(ctx, md.ElapsedTime/1000, opts["new"].MeasurementOption())
300
301 if s.duplicate {
302 s.requestBytesCounter.Add(ctx, md.RequestSize, opts["old"].AddOptions())
303 s.latencyMeasure.Record(ctx, md.ElapsedTime, opts["old"].MeasurementOption())
304 }
305 }
306
307 func (s HTTPClient) RecordResponseSize(ctx context.Context, responseData int64, opts map[string]MetricOpts) {
308 if s.responseBytesCounter == nil {
309 // This will happen if an HTTPClient{} is used instead of NewHTTPClient().
310 return
311 }
312
313 s.responseBytesCounter.Add(ctx, responseData, opts["old"].AddOptions())
314 }
315
316 func (s HTTPClient) TraceAttributes(host string) []attribute.KeyValue {
317 attrs := CurrentHTTPClient{}.TraceAttributes(host)
318 if s.duplicate {
319 return OldHTTPClient{}.TraceAttributes(host, attrs)
320 }
321
322 return attrs
323 }
324