httpconv.go raw

   1  // Code generated by gotmpl. DO NOT MODIFY.
   2  // source: internal/shared/semconv/httpconv.go.tmpl
   3  
   4  // Copyright The OpenTelemetry Authors
   5  // SPDX-License-Identifier: Apache-2.0
   6  
   7  // Package semconv provides OpenTelemetry semantic convention types and
   8  // functionality.
   9  package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
  10  
  11  import (
  12  	"fmt"
  13  	"net/http"
  14  	"reflect"
  15  	"slices"
  16  	"strconv"
  17  	"strings"
  18  
  19  	"go.opentelemetry.io/otel/attribute"
  20  	"go.opentelemetry.io/otel/metric"
  21  	"go.opentelemetry.io/otel/metric/noop"
  22  	semconvNew "go.opentelemetry.io/otel/semconv/v1.26.0"
  23  )
  24  
  25  type RequestTraceAttrsOpts struct {
  26  	// If set, this is used as value for the "http.client_ip" attribute.
  27  	HTTPClientIP string
  28  }
  29  
  30  type CurrentHTTPServer struct{}
  31  
  32  // RequestTraceAttrs returns trace attributes for an HTTP request received by a
  33  // server.
  34  //
  35  // The server must be the primary server name if it is known. For example this
  36  // would be the ServerName directive
  37  // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
  38  // server, and the server_name directive
  39  // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
  40  // nginx server. More generically, the primary server name would be the host
  41  // header value that matches the default virtual host of an HTTP server. It
  42  // should include the host identifier and if a port is used to route to the
  43  // server that port identifier should be included as an appropriate port
  44  // suffix.
  45  //
  46  // If the primary server name is not known, server should be an empty string.
  47  // The req Host will be used to determine the server instead.
  48  func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
  49  	count := 3 // ServerAddress, Method, Scheme
  50  
  51  	var host string
  52  	var p int
  53  	if server == "" {
  54  		host, p = SplitHostPort(req.Host)
  55  	} else {
  56  		// Prioritize the primary server name.
  57  		host, p = SplitHostPort(server)
  58  		if p < 0 {
  59  			_, p = SplitHostPort(req.Host)
  60  		}
  61  	}
  62  
  63  	hostPort := requiredHTTPPort(req.TLS != nil, p)
  64  	if hostPort > 0 {
  65  		count++
  66  	}
  67  
  68  	method, methodOriginal := n.method(req.Method)
  69  	if methodOriginal != (attribute.KeyValue{}) {
  70  		count++
  71  	}
  72  
  73  	scheme := n.scheme(req.TLS != nil)
  74  
  75  	peer, peerPort := SplitHostPort(req.RemoteAddr)
  76  	if peer != "" {
  77  		// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
  78  		// file-path that would be interpreted with a sock family.
  79  		count++
  80  		if peerPort > 0 {
  81  			count++
  82  		}
  83  	}
  84  
  85  	useragent := req.UserAgent()
  86  	if useragent != "" {
  87  		count++
  88  	}
  89  
  90  	// For client IP, use, in order:
  91  	// 1. The value passed in the options
  92  	// 2. The value in the X-Forwarded-For header
  93  	// 3. The peer address
  94  	clientIP := opts.HTTPClientIP
  95  	if clientIP == "" {
  96  		clientIP = serverClientIP(req.Header.Get("X-Forwarded-For"))
  97  		if clientIP == "" {
  98  			clientIP = peer
  99  		}
 100  	}
 101  	if clientIP != "" {
 102  		count++
 103  	}
 104  
 105  	if req.URL != nil && req.URL.Path != "" {
 106  		count++
 107  	}
 108  
 109  	protoName, protoVersion := netProtocol(req.Proto)
 110  	if protoName != "" && protoName != "http" {
 111  		count++
 112  	}
 113  	if protoVersion != "" {
 114  		count++
 115  	}
 116  
 117  	route := httpRoute(req.Pattern)
 118  	if route != "" {
 119  		count++
 120  	}
 121  
 122  	attrs := make([]attribute.KeyValue, 0, count)
 123  	attrs = append(attrs,
 124  		semconvNew.ServerAddress(host),
 125  		method,
 126  		scheme,
 127  	)
 128  
 129  	if hostPort > 0 {
 130  		attrs = append(attrs, semconvNew.ServerPort(hostPort))
 131  	}
 132  	if methodOriginal != (attribute.KeyValue{}) {
 133  		attrs = append(attrs, methodOriginal)
 134  	}
 135  
 136  	if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" {
 137  		// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
 138  		// file-path that would be interpreted with a sock family.
 139  		attrs = append(attrs, semconvNew.NetworkPeerAddress(peer))
 140  		if peerPort > 0 {
 141  			attrs = append(attrs, semconvNew.NetworkPeerPort(peerPort))
 142  		}
 143  	}
 144  
 145  	if useragent != "" {
 146  		attrs = append(attrs, semconvNew.UserAgentOriginal(useragent))
 147  	}
 148  
 149  	if clientIP != "" {
 150  		attrs = append(attrs, semconvNew.ClientAddress(clientIP))
 151  	}
 152  
 153  	if req.URL != nil && req.URL.Path != "" {
 154  		attrs = append(attrs, semconvNew.URLPath(req.URL.Path))
 155  	}
 156  
 157  	if protoName != "" && protoName != "http" {
 158  		attrs = append(attrs, semconvNew.NetworkProtocolName(protoName))
 159  	}
 160  	if protoVersion != "" {
 161  		attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion))
 162  	}
 163  
 164  	if route != "" {
 165  		attrs = append(attrs, n.Route(route))
 166  	}
 167  
 168  	return attrs
 169  }
 170  
 171  func (n CurrentHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {
 172  	switch network {
 173  	case "tcp", "tcp4", "tcp6":
 174  		return semconvNew.NetworkTransportTCP
 175  	case "udp", "udp4", "udp6":
 176  		return semconvNew.NetworkTransportUDP
 177  	case "unix", "unixgram", "unixpacket":
 178  		return semconvNew.NetworkTransportUnix
 179  	default:
 180  		return semconvNew.NetworkTransportPipe
 181  	}
 182  }
 183  
 184  func (n CurrentHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) {
 185  	if method == "" {
 186  		return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
 187  	}
 188  	if attr, ok := methodLookup[method]; ok {
 189  		return attr, attribute.KeyValue{}
 190  	}
 191  
 192  	orig := semconvNew.HTTPRequestMethodOriginal(method)
 193  	if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
 194  		return attr, orig
 195  	}
 196  	return semconvNew.HTTPRequestMethodGet, orig
 197  }
 198  
 199  func (n CurrentHTTPServer) scheme(https bool) attribute.KeyValue { // nolint:revive
 200  	if https {
 201  		return semconvNew.URLScheme("https")
 202  	}
 203  	return semconvNew.URLScheme("http")
 204  }
 205  
 206  // ResponseTraceAttrs returns trace attributes for telemetry from an HTTP
 207  // response.
 208  //
 209  // If any of the fields in the ResponseTelemetry are not set the attribute will
 210  // be omitted.
 211  func (n CurrentHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
 212  	var count int
 213  
 214  	if resp.ReadBytes > 0 {
 215  		count++
 216  	}
 217  	if resp.WriteBytes > 0 {
 218  		count++
 219  	}
 220  	if resp.StatusCode > 0 {
 221  		count++
 222  	}
 223  
 224  	attributes := make([]attribute.KeyValue, 0, count)
 225  
 226  	if resp.ReadBytes > 0 {
 227  		attributes = append(attributes,
 228  			semconvNew.HTTPRequestBodySize(int(resp.ReadBytes)),
 229  		)
 230  	}
 231  	if resp.WriteBytes > 0 {
 232  		attributes = append(attributes,
 233  			semconvNew.HTTPResponseBodySize(int(resp.WriteBytes)),
 234  		)
 235  	}
 236  	if resp.StatusCode > 0 {
 237  		attributes = append(attributes,
 238  			semconvNew.HTTPResponseStatusCode(resp.StatusCode),
 239  		)
 240  	}
 241  
 242  	return attributes
 243  }
 244  
 245  // Route returns the attribute for the route.
 246  func (n CurrentHTTPServer) Route(route string) attribute.KeyValue {
 247  	return semconvNew.HTTPRoute(route)
 248  }
 249  
 250  func (n CurrentHTTPServer) createMeasures(meter metric.Meter) (metric.Int64Histogram, metric.Int64Histogram, metric.Float64Histogram) {
 251  	if meter == nil {
 252  		return noop.Int64Histogram{}, noop.Int64Histogram{}, noop.Float64Histogram{}
 253  	}
 254  
 255  	var err error
 256  	requestBodySizeHistogram, err := meter.Int64Histogram(
 257  		semconvNew.HTTPServerRequestBodySizeName,
 258  		metric.WithUnit(semconvNew.HTTPServerRequestBodySizeUnit),
 259  		metric.WithDescription(semconvNew.HTTPServerRequestBodySizeDescription),
 260  	)
 261  	handleErr(err)
 262  
 263  	responseBodySizeHistogram, err := meter.Int64Histogram(
 264  		semconvNew.HTTPServerResponseBodySizeName,
 265  		metric.WithUnit(semconvNew.HTTPServerResponseBodySizeUnit),
 266  		metric.WithDescription(semconvNew.HTTPServerResponseBodySizeDescription),
 267  	)
 268  	handleErr(err)
 269  	requestDurationHistogram, err := meter.Float64Histogram(
 270  		semconvNew.HTTPServerRequestDurationName,
 271  		metric.WithUnit(semconvNew.HTTPServerRequestDurationUnit),
 272  		metric.WithDescription(semconvNew.HTTPServerRequestDurationDescription),
 273  		metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10),
 274  	)
 275  	handleErr(err)
 276  
 277  	return requestBodySizeHistogram, responseBodySizeHistogram, requestDurationHistogram
 278  }
 279  
 280  func (n CurrentHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
 281  	num := len(additionalAttributes) + 3
 282  	var host string
 283  	var p int
 284  	if server == "" {
 285  		host, p = SplitHostPort(req.Host)
 286  	} else {
 287  		// Prioritize the primary server name.
 288  		host, p = SplitHostPort(server)
 289  		if p < 0 {
 290  			_, p = SplitHostPort(req.Host)
 291  		}
 292  	}
 293  	hostPort := requiredHTTPPort(req.TLS != nil, p)
 294  	if hostPort > 0 {
 295  		num++
 296  	}
 297  	protoName, protoVersion := netProtocol(req.Proto)
 298  	if protoName != "" {
 299  		num++
 300  	}
 301  	if protoVersion != "" {
 302  		num++
 303  	}
 304  
 305  	if statusCode > 0 {
 306  		num++
 307  	}
 308  
 309  	attributes := slices.Grow(additionalAttributes, num)
 310  	attributes = append(attributes,
 311  		semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
 312  		n.scheme(req.TLS != nil),
 313  		semconvNew.ServerAddress(host))
 314  
 315  	if hostPort > 0 {
 316  		attributes = append(attributes, semconvNew.ServerPort(hostPort))
 317  	}
 318  	if protoName != "" {
 319  		attributes = append(attributes, semconvNew.NetworkProtocolName(protoName))
 320  	}
 321  	if protoVersion != "" {
 322  		attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion))
 323  	}
 324  
 325  	if statusCode > 0 {
 326  		attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode))
 327  	}
 328  	return attributes
 329  }
 330  
 331  type CurrentHTTPClient struct{}
 332  
 333  // RequestTraceAttrs returns trace attributes for an HTTP request made by a client.
 334  func (n CurrentHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
 335  	/*
 336  	   below attributes are returned:
 337  	   - http.request.method
 338  	   - http.request.method.original
 339  	   - url.full
 340  	   - server.address
 341  	   - server.port
 342  	   - network.protocol.name
 343  	   - network.protocol.version
 344  	*/
 345  	numOfAttributes := 3 // URL, server address, proto, and method.
 346  
 347  	var urlHost string
 348  	if req.URL != nil {
 349  		urlHost = req.URL.Host
 350  	}
 351  	var requestHost string
 352  	var requestPort int
 353  	for _, hostport := range []string{urlHost, req.Header.Get("Host")} {
 354  		requestHost, requestPort = SplitHostPort(hostport)
 355  		if requestHost != "" || requestPort > 0 {
 356  			break
 357  		}
 358  	}
 359  
 360  	eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
 361  	if eligiblePort > 0 {
 362  		numOfAttributes++
 363  	}
 364  	useragent := req.UserAgent()
 365  	if useragent != "" {
 366  		numOfAttributes++
 367  	}
 368  
 369  	protoName, protoVersion := netProtocol(req.Proto)
 370  	if protoName != "" && protoName != "http" {
 371  		numOfAttributes++
 372  	}
 373  	if protoVersion != "" {
 374  		numOfAttributes++
 375  	}
 376  
 377  	method, originalMethod := n.method(req.Method)
 378  	if originalMethod != (attribute.KeyValue{}) {
 379  		numOfAttributes++
 380  	}
 381  
 382  	attrs := make([]attribute.KeyValue, 0, numOfAttributes)
 383  
 384  	attrs = append(attrs, method)
 385  	if originalMethod != (attribute.KeyValue{}) {
 386  		attrs = append(attrs, originalMethod)
 387  	}
 388  
 389  	var u string
 390  	if req.URL != nil {
 391  		// Remove any username/password info that may be in the URL.
 392  		userinfo := req.URL.User
 393  		req.URL.User = nil
 394  		u = req.URL.String()
 395  		// Restore any username/password info that was removed.
 396  		req.URL.User = userinfo
 397  	}
 398  	attrs = append(attrs, semconvNew.URLFull(u))
 399  
 400  	attrs = append(attrs, semconvNew.ServerAddress(requestHost))
 401  	if eligiblePort > 0 {
 402  		attrs = append(attrs, semconvNew.ServerPort(eligiblePort))
 403  	}
 404  
 405  	if protoName != "" && protoName != "http" {
 406  		attrs = append(attrs, semconvNew.NetworkProtocolName(protoName))
 407  	}
 408  	if protoVersion != "" {
 409  		attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion))
 410  	}
 411  
 412  	return attrs
 413  }
 414  
 415  // ResponseTraceAttrs returns trace attributes for an HTTP response made by a client.
 416  func (n CurrentHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
 417  	/*
 418  	   below attributes are returned:
 419  	   - http.response.status_code
 420  	   - error.type
 421  	*/
 422  	var count int
 423  	if resp.StatusCode > 0 {
 424  		count++
 425  	}
 426  
 427  	if isErrorStatusCode(resp.StatusCode) {
 428  		count++
 429  	}
 430  
 431  	attrs := make([]attribute.KeyValue, 0, count)
 432  	if resp.StatusCode > 0 {
 433  		attrs = append(attrs, semconvNew.HTTPResponseStatusCode(resp.StatusCode))
 434  	}
 435  
 436  	if isErrorStatusCode(resp.StatusCode) {
 437  		errorType := strconv.Itoa(resp.StatusCode)
 438  		attrs = append(attrs, semconvNew.ErrorTypeKey.String(errorType))
 439  	}
 440  	return attrs
 441  }
 442  
 443  func (n CurrentHTTPClient) ErrorType(err error) attribute.KeyValue {
 444  	t := reflect.TypeOf(err)
 445  	var value string
 446  	if t.PkgPath() == "" && t.Name() == "" {
 447  		// Likely a builtin type.
 448  		value = t.String()
 449  	} else {
 450  		value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
 451  	}
 452  
 453  	if value == "" {
 454  		return semconvNew.ErrorTypeOther
 455  	}
 456  
 457  	return semconvNew.ErrorTypeKey.String(value)
 458  }
 459  
 460  func (n CurrentHTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) {
 461  	if method == "" {
 462  		return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
 463  	}
 464  	if attr, ok := methodLookup[method]; ok {
 465  		return attr, attribute.KeyValue{}
 466  	}
 467  
 468  	orig := semconvNew.HTTPRequestMethodOriginal(method)
 469  	if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
 470  		return attr, orig
 471  	}
 472  	return semconvNew.HTTPRequestMethodGet, orig
 473  }
 474  
 475  func (n CurrentHTTPClient) createMeasures(meter metric.Meter) (metric.Int64Histogram, metric.Float64Histogram) {
 476  	if meter == nil {
 477  		return noop.Int64Histogram{}, noop.Float64Histogram{}
 478  	}
 479  
 480  	var err error
 481  	requestBodySize, err := meter.Int64Histogram(
 482  		semconvNew.HTTPClientRequestBodySizeName,
 483  		metric.WithUnit(semconvNew.HTTPClientRequestBodySizeUnit),
 484  		metric.WithDescription(semconvNew.HTTPClientRequestBodySizeDescription),
 485  	)
 486  	handleErr(err)
 487  
 488  	requestDuration, err := meter.Float64Histogram(
 489  		semconvNew.HTTPClientRequestDurationName,
 490  		metric.WithUnit(semconvNew.HTTPClientRequestDurationUnit),
 491  		metric.WithDescription(semconvNew.HTTPClientRequestDurationDescription),
 492  		metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10),
 493  	)
 494  	handleErr(err)
 495  
 496  	return requestBodySize, requestDuration
 497  }
 498  
 499  func (n CurrentHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
 500  	num := len(additionalAttributes) + 2
 501  	var h string
 502  	if req.URL != nil {
 503  		h = req.URL.Host
 504  	}
 505  	var requestHost string
 506  	var requestPort int
 507  	for _, hostport := range []string{h, req.Header.Get("Host")} {
 508  		requestHost, requestPort = SplitHostPort(hostport)
 509  		if requestHost != "" || requestPort > 0 {
 510  			break
 511  		}
 512  	}
 513  
 514  	port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
 515  	if port > 0 {
 516  		num++
 517  	}
 518  
 519  	protoName, protoVersion := netProtocol(req.Proto)
 520  	if protoName != "" {
 521  		num++
 522  	}
 523  	if protoVersion != "" {
 524  		num++
 525  	}
 526  
 527  	if statusCode > 0 {
 528  		num++
 529  	}
 530  
 531  	attributes := slices.Grow(additionalAttributes, num)
 532  	attributes = append(attributes,
 533  		semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
 534  		semconvNew.ServerAddress(requestHost),
 535  		n.scheme(req),
 536  	)
 537  
 538  	if port > 0 {
 539  		attributes = append(attributes, semconvNew.ServerPort(port))
 540  	}
 541  	if protoName != "" {
 542  		attributes = append(attributes, semconvNew.NetworkProtocolName(protoName))
 543  	}
 544  	if protoVersion != "" {
 545  		attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion))
 546  	}
 547  
 548  	if statusCode > 0 {
 549  		attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode))
 550  	}
 551  	return attributes
 552  }
 553  
 554  // TraceAttributes returns attributes for httptrace.
 555  func (n CurrentHTTPClient) TraceAttributes(host string) []attribute.KeyValue {
 556  	return []attribute.KeyValue{
 557  		semconvNew.ServerAddress(host),
 558  	}
 559  }
 560  
 561  func (n CurrentHTTPClient) scheme(req *http.Request) attribute.KeyValue {
 562  	if req.URL != nil && req.URL.Scheme != "" {
 563  		return semconvNew.URLScheme(req.URL.Scheme)
 564  	}
 565  	if req.TLS != nil {
 566  		return semconvNew.URLScheme("https")
 567  	}
 568  	return semconvNew.URLScheme("http")
 569  }
 570  
 571  func isErrorStatusCode(code int) bool {
 572  	return code >= 400 || code < 100
 573  }
 574