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