env.go

  1// Copyright The OpenTelemetry Authors
  2// SPDX-License-Identifier: Apache-2.0
  3
  4package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
  5
  6import (
  7	"context"
  8	"fmt"
  9	"net/http"
 10	"os"
 11	"strings"
 12
 13	"go.opentelemetry.io/otel/attribute"
 14	"go.opentelemetry.io/otel/codes"
 15	"go.opentelemetry.io/otel/metric"
 16)
 17
 18type ResponseTelemetry struct {
 19	StatusCode int
 20	ReadBytes  int64
 21	ReadError  error
 22	WriteBytes int64
 23	WriteError error
 24}
 25
 26type HTTPServer struct {
 27	duplicate bool
 28
 29	// Old metrics
 30	requestBytesCounter  metric.Int64Counter
 31	responseBytesCounter metric.Int64Counter
 32	serverLatencyMeasure metric.Float64Histogram
 33}
 34
 35// RequestTraceAttrs returns trace attributes for an HTTP request received by a
 36// server.
 37//
 38// The server must be the primary server name if it is known. For example this
 39// would be the ServerName directive
 40// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
 41// server, and the server_name directive
 42// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
 43// nginx server. More generically, the primary server name would be the host
 44// header value that matches the default virtual host of an HTTP server. It
 45// should include the host identifier and if a port is used to route to the
 46// server that port identifier should be included as an appropriate port
 47// suffix.
 48//
 49// If the primary server name is not known, server should be an empty string.
 50// The req Host will be used to determine the server instead.
 51func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
 52	if s.duplicate {
 53		return append(oldHTTPServer{}.RequestTraceAttrs(server, req), newHTTPServer{}.RequestTraceAttrs(server, req)...)
 54	}
 55	return oldHTTPServer{}.RequestTraceAttrs(server, req)
 56}
 57
 58// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response.
 59//
 60// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
 61func (s HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
 62	if s.duplicate {
 63		return append(oldHTTPServer{}.ResponseTraceAttrs(resp), newHTTPServer{}.ResponseTraceAttrs(resp)...)
 64	}
 65	return oldHTTPServer{}.ResponseTraceAttrs(resp)
 66}
 67
 68// Route returns the attribute for the route.
 69func (s HTTPServer) Route(route string) attribute.KeyValue {
 70	return oldHTTPServer{}.Route(route)
 71}
 72
 73// Status returns a span status code and message for an HTTP status code
 74// value returned by a server. Status codes in the 400-499 range are not
 75// returned as errors.
 76func (s HTTPServer) Status(code int) (codes.Code, string) {
 77	if code < 100 || code >= 600 {
 78		return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
 79	}
 80	if code >= 500 {
 81		return codes.Error, ""
 82	}
 83	return codes.Unset, ""
 84}
 85
 86type MetricData struct {
 87	ServerName           string
 88	Req                  *http.Request
 89	StatusCode           int
 90	AdditionalAttributes []attribute.KeyValue
 91
 92	RequestSize  int64
 93	ResponseSize int64
 94	ElapsedTime  float64
 95}
 96
 97func (s HTTPServer) RecordMetrics(ctx context.Context, md MetricData) {
 98	if s.requestBytesCounter == nil || s.responseBytesCounter == nil || s.serverLatencyMeasure == nil {
 99		// This will happen if an HTTPServer{} is used insted of NewHTTPServer.
100		return
101	}
102
103	attributes := oldHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
104	o := metric.WithAttributeSet(attribute.NewSet(attributes...))
105	addOpts := []metric.AddOption{o} // Allocate vararg slice once.
106	s.requestBytesCounter.Add(ctx, md.RequestSize, addOpts...)
107	s.responseBytesCounter.Add(ctx, md.ResponseSize, addOpts...)
108	s.serverLatencyMeasure.Record(ctx, md.ElapsedTime, o)
109
110	// TODO: Duplicate Metrics
111}
112
113func NewHTTPServer(meter metric.Meter) HTTPServer {
114	env := strings.ToLower(os.Getenv("OTEL_SEMCONV_STABILITY_OPT_IN"))
115	duplicate := env == "http/dup"
116	server := HTTPServer{
117		duplicate: duplicate,
118	}
119	server.requestBytesCounter, server.responseBytesCounter, server.serverLatencyMeasure = oldHTTPServer{}.createMeasures(meter)
120	return server
121}
122
123type HTTPClient struct {
124	duplicate bool
125}
126
127func NewHTTPClient() HTTPClient {
128	env := strings.ToLower(os.Getenv("OTEL_SEMCONV_STABILITY_OPT_IN"))
129	return HTTPClient{duplicate: env == "http/dup"}
130}
131
132// RequestTraceAttrs returns attributes for an HTTP request made by a client.
133func (c HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
134	if c.duplicate {
135		return append(oldHTTPClient{}.RequestTraceAttrs(req), newHTTPClient{}.RequestTraceAttrs(req)...)
136	}
137	return oldHTTPClient{}.RequestTraceAttrs(req)
138}
139
140// ResponseTraceAttrs returns metric attributes for an HTTP request made by a client.
141func (c HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
142	if c.duplicate {
143		return append(oldHTTPClient{}.ResponseTraceAttrs(resp), newHTTPClient{}.ResponseTraceAttrs(resp)...)
144	}
145
146	return oldHTTPClient{}.ResponseTraceAttrs(resp)
147}
148
149func (c HTTPClient) Status(code int) (codes.Code, string) {
150	if code < 100 || code >= 600 {
151		return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
152	}
153	if code >= 400 {
154		return codes.Error, ""
155	}
156	return codes.Unset, ""
157}
158
159func (c HTTPClient) ErrorType(err error) attribute.KeyValue {
160	if c.duplicate {
161		return newHTTPClient{}.ErrorType(err)
162	}
163
164	return attribute.KeyValue{}
165}