...

Source file src/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go

Documentation: go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil

     1  // Code created by gotmpl. DO NOT MODIFY.
     2  // source: internal/shared/semconvutil/httpconv.go.tmpl
     3  
     4  // Copyright The OpenTelemetry Authors
     5  // SPDX-License-Identifier: Apache-2.0
     6  
     7  package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
     8  
     9  import (
    10  	"fmt"
    11  	"net/http"
    12  	"strings"
    13  
    14  	"go.opentelemetry.io/otel/attribute"
    15  	"go.opentelemetry.io/otel/codes"
    16  	semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
    17  )
    18  
    19  // HTTPClientResponse returns trace attributes for an HTTP response received by a
    20  // client from a server. It will return the following attributes if the related
    21  // values are defined in resp: "http.status.code",
    22  // "http.response_content_length".
    23  //
    24  // This does not add all OpenTelemetry required attributes for an HTTP event,
    25  // it assumes ClientRequest was used to create the span with a complete set of
    26  // attributes. If a complete set of attributes can be generated using the
    27  // request contained in resp. For example:
    28  //
    29  //	append(HTTPClientResponse(resp), ClientRequest(resp.Request)...)
    30  func HTTPClientResponse(resp *http.Response) []attribute.KeyValue {
    31  	return hc.ClientResponse(resp)
    32  }
    33  
    34  // HTTPClientRequest returns trace attributes for an HTTP request made by a client.
    35  // The following attributes are always returned: "http.url", "http.method",
    36  // "net.peer.name". The following attributes are returned if the related values
    37  // are defined in req: "net.peer.port", "user_agent.original",
    38  // "http.request_content_length".
    39  func HTTPClientRequest(req *http.Request) []attribute.KeyValue {
    40  	return hc.ClientRequest(req)
    41  }
    42  
    43  // HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client.
    44  // The following attributes are always returned: "http.method", "net.peer.name".
    45  // The following attributes are returned if the
    46  // related values are defined in req: "net.peer.port".
    47  func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue {
    48  	return hc.ClientRequestMetrics(req)
    49  }
    50  
    51  // HTTPClientStatus returns a span status code and message for an HTTP status code
    52  // value received by a client.
    53  func HTTPClientStatus(code int) (codes.Code, string) {
    54  	return hc.ClientStatus(code)
    55  }
    56  
    57  // HTTPServerRequest returns trace attributes for an HTTP request received by a
    58  // server.
    59  //
    60  // The server must be the primary server name if it is known. For example this
    61  // would be the ServerName directive
    62  // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
    63  // server, and the server_name directive
    64  // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
    65  // nginx server. More generically, the primary server name would be the host
    66  // header value that matches the default virtual host of an HTTP server. It
    67  // should include the host identifier and if a port is used to route to the
    68  // server that port identifier should be included as an appropriate port
    69  // suffix.
    70  //
    71  // If the primary server name is not known, server should be an empty string.
    72  // The req Host will be used to determine the server instead.
    73  //
    74  // The following attributes are always returned: "http.method", "http.scheme",
    75  // "http.target", "net.host.name". The following attributes are returned if
    76  // they related values are defined in req: "net.host.port", "net.sock.peer.addr",
    77  // "net.sock.peer.port", "user_agent.original", "http.client_ip".
    78  func HTTPServerRequest(server string, req *http.Request) []attribute.KeyValue {
    79  	return hc.ServerRequest(server, req)
    80  }
    81  
    82  // HTTPServerRequestMetrics returns metric attributes for an HTTP request received by a
    83  // server.
    84  //
    85  // The server must be the primary server name if it is known. For example this
    86  // would be the ServerName directive
    87  // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
    88  // server, and the server_name directive
    89  // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
    90  // nginx server. More generically, the primary server name would be the host
    91  // header value that matches the default virtual host of an HTTP server. It
    92  // should include the host identifier and if a port is used to route to the
    93  // server that port identifier should be included as an appropriate port
    94  // suffix.
    95  //
    96  // If the primary server name is not known, server should be an empty string.
    97  // The req Host will be used to determine the server instead.
    98  //
    99  // The following attributes are always returned: "http.method", "http.scheme",
   100  // "net.host.name". The following attributes are returned if they related
   101  // values are defined in req: "net.host.port".
   102  func HTTPServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue {
   103  	return hc.ServerRequestMetrics(server, req)
   104  }
   105  
   106  // HTTPServerStatus returns a span status code and message for an HTTP status code
   107  // value returned by a server. Status codes in the 400-499 range are not
   108  // returned as errors.
   109  func HTTPServerStatus(code int) (codes.Code, string) {
   110  	return hc.ServerStatus(code)
   111  }
   112  
   113  // httpConv are the HTTP semantic convention attributes defined for a version
   114  // of the OpenTelemetry specification.
   115  type httpConv struct {
   116  	NetConv *netConv
   117  
   118  	HTTPClientIPKey              attribute.Key
   119  	HTTPMethodKey                attribute.Key
   120  	HTTPRequestContentLengthKey  attribute.Key
   121  	HTTPResponseContentLengthKey attribute.Key
   122  	HTTPRouteKey                 attribute.Key
   123  	HTTPSchemeHTTP               attribute.KeyValue
   124  	HTTPSchemeHTTPS              attribute.KeyValue
   125  	HTTPStatusCodeKey            attribute.Key
   126  	HTTPTargetKey                attribute.Key
   127  	HTTPURLKey                   attribute.Key
   128  	UserAgentOriginalKey         attribute.Key
   129  }
   130  
   131  var hc = &httpConv{
   132  	NetConv: nc,
   133  
   134  	HTTPClientIPKey:              semconv.HTTPClientIPKey,
   135  	HTTPMethodKey:                semconv.HTTPMethodKey,
   136  	HTTPRequestContentLengthKey:  semconv.HTTPRequestContentLengthKey,
   137  	HTTPResponseContentLengthKey: semconv.HTTPResponseContentLengthKey,
   138  	HTTPRouteKey:                 semconv.HTTPRouteKey,
   139  	HTTPSchemeHTTP:               semconv.HTTPSchemeHTTP,
   140  	HTTPSchemeHTTPS:              semconv.HTTPSchemeHTTPS,
   141  	HTTPStatusCodeKey:            semconv.HTTPStatusCodeKey,
   142  	HTTPTargetKey:                semconv.HTTPTargetKey,
   143  	HTTPURLKey:                   semconv.HTTPURLKey,
   144  	UserAgentOriginalKey:         semconv.UserAgentOriginalKey,
   145  }
   146  
   147  // ClientResponse returns attributes for an HTTP response received by a client
   148  // from a server. The following attributes are returned if the related values
   149  // are defined in resp: "http.status.code", "http.response_content_length".
   150  //
   151  // This does not add all OpenTelemetry required attributes for an HTTP event,
   152  // it assumes ClientRequest was used to create the span with a complete set of
   153  // attributes. If a complete set of attributes can be generated using the
   154  // request contained in resp. For example:
   155  //
   156  //	append(ClientResponse(resp), ClientRequest(resp.Request)...)
   157  func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
   158  	/* The following semantic conventions are returned if present:
   159  	http.status_code                int
   160  	http.response_content_length    int
   161  	*/
   162  	var n int
   163  	if resp.StatusCode > 0 {
   164  		n++
   165  	}
   166  	if resp.ContentLength > 0 {
   167  		n++
   168  	}
   169  
   170  	attrs := make([]attribute.KeyValue, 0, n)
   171  	if resp.StatusCode > 0 {
   172  		attrs = append(attrs, c.HTTPStatusCodeKey.Int(resp.StatusCode))
   173  	}
   174  	if resp.ContentLength > 0 {
   175  		attrs = append(attrs, c.HTTPResponseContentLengthKey.Int(int(resp.ContentLength)))
   176  	}
   177  	return attrs
   178  }
   179  
   180  // ClientRequest returns attributes for an HTTP request made by a client. The
   181  // following attributes are always returned: "http.url", "http.method",
   182  // "net.peer.name". The following attributes are returned if the related values
   183  // are defined in req: "net.peer.port", "user_agent.original",
   184  // "http.request_content_length", "user_agent.original".
   185  func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
   186  	/* The following semantic conventions are returned if present:
   187  	http.method                     string
   188  	user_agent.original             string
   189  	http.url                        string
   190  	net.peer.name                   string
   191  	net.peer.port                   int
   192  	http.request_content_length     int
   193  	*/
   194  
   195  	/* The following semantic conventions are not returned:
   196  	http.status_code                This requires the response. See ClientResponse.
   197  	http.response_content_length    This requires the response. See ClientResponse.
   198  	net.sock.family                 This requires the socket used.
   199  	net.sock.peer.addr              This requires the socket used.
   200  	net.sock.peer.name              This requires the socket used.
   201  	net.sock.peer.port              This requires the socket used.
   202  	http.resend_count               This is something outside of a single request.
   203  	net.protocol.name               The value is the Request is ignored, and the go client will always use "http".
   204  	net.protocol.version            The value in the Request is ignored, and the go client will always use 1.1 or 2.0.
   205  	*/
   206  	n := 3 // URL, peer name, proto, and method.
   207  	var h string
   208  	if req.URL != nil {
   209  		h = req.URL.Host
   210  	}
   211  	peer, p := firstHostPort(h, req.Header.Get("Host"))
   212  	port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p)
   213  	if port > 0 {
   214  		n++
   215  	}
   216  	useragent := req.UserAgent()
   217  	if useragent != "" {
   218  		n++
   219  	}
   220  	if req.ContentLength > 0 {
   221  		n++
   222  	}
   223  
   224  	attrs := make([]attribute.KeyValue, 0, n)
   225  
   226  	attrs = append(attrs, c.method(req.Method))
   227  
   228  	var u string
   229  	if req.URL != nil {
   230  		// Remove any username/password info that may be in the URL.
   231  		userinfo := req.URL.User
   232  		req.URL.User = nil
   233  		u = req.URL.String()
   234  		// Restore any username/password info that was removed.
   235  		req.URL.User = userinfo
   236  	}
   237  	attrs = append(attrs, c.HTTPURLKey.String(u))
   238  
   239  	attrs = append(attrs, c.NetConv.PeerName(peer))
   240  	if port > 0 {
   241  		attrs = append(attrs, c.NetConv.PeerPort(port))
   242  	}
   243  
   244  	if useragent != "" {
   245  		attrs = append(attrs, c.UserAgentOriginalKey.String(useragent))
   246  	}
   247  
   248  	if l := req.ContentLength; l > 0 {
   249  		attrs = append(attrs, c.HTTPRequestContentLengthKey.Int64(l))
   250  	}
   251  
   252  	return attrs
   253  }
   254  
   255  // ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The
   256  // following attributes are always returned: "http.method", "net.peer.name".
   257  // The following attributes are returned if the related values
   258  // are defined in req: "net.peer.port".
   259  func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue {
   260  	/* The following semantic conventions are returned if present:
   261  	http.method                     string
   262  	net.peer.name                   string
   263  	net.peer.port                   int
   264  	*/
   265  
   266  	n := 2 // method, peer name.
   267  	var h string
   268  	if req.URL != nil {
   269  		h = req.URL.Host
   270  	}
   271  	peer, p := firstHostPort(h, req.Header.Get("Host"))
   272  	port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p)
   273  	if port > 0 {
   274  		n++
   275  	}
   276  
   277  	attrs := make([]attribute.KeyValue, 0, n)
   278  	attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer))
   279  
   280  	if port > 0 {
   281  		attrs = append(attrs, c.NetConv.PeerPort(port))
   282  	}
   283  
   284  	return attrs
   285  }
   286  
   287  // ServerRequest returns attributes for an HTTP request received by a server.
   288  //
   289  // The server must be the primary server name if it is known. For example this
   290  // would be the ServerName directive
   291  // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
   292  // server, and the server_name directive
   293  // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
   294  // nginx server. More generically, the primary server name would be the host
   295  // header value that matches the default virtual host of an HTTP server. It
   296  // should include the host identifier and if a port is used to route to the
   297  // server that port identifier should be included as an appropriate port
   298  // suffix.
   299  //
   300  // If the primary server name is not known, server should be an empty string.
   301  // The req Host will be used to determine the server instead.
   302  //
   303  // The following attributes are always returned: "http.method", "http.scheme",
   304  // "http.target", "net.host.name". The following attributes are returned if they
   305  // related values are defined in req: "net.host.port", "net.sock.peer.addr",
   306  // "net.sock.peer.port", "user_agent.original", "http.client_ip",
   307  // "net.protocol.name", "net.protocol.version".
   308  func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.KeyValue {
   309  	/* The following semantic conventions are returned if present:
   310  	http.method             string
   311  	http.scheme             string
   312  	net.host.name           string
   313  	net.host.port           int
   314  	net.sock.peer.addr      string
   315  	net.sock.peer.port      int
   316  	user_agent.original     string
   317  	http.client_ip          string
   318  	net.protocol.name       string Note: not set if the value is "http".
   319  	net.protocol.version    string
   320  	http.target             string Note: doesn't include the query parameter.
   321  	*/
   322  
   323  	/* The following semantic conventions are not returned:
   324  	http.status_code                This requires the response.
   325  	http.request_content_length     This requires the len() of body, which can mutate it.
   326  	http.response_content_length    This requires the response.
   327  	http.route                      This is not available.
   328  	net.sock.peer.name              This would require a DNS lookup.
   329  	net.sock.host.addr              The request doesn't have access to the underlying socket.
   330  	net.sock.host.port              The request doesn't have access to the underlying socket.
   331  
   332  	*/
   333  	n := 4 // Method, scheme, proto, and host name.
   334  	var host string
   335  	var p int
   336  	if server == "" {
   337  		host, p = splitHostPort(req.Host)
   338  	} else {
   339  		// Prioritize the primary server name.
   340  		host, p = splitHostPort(server)
   341  		if p < 0 {
   342  			_, p = splitHostPort(req.Host)
   343  		}
   344  	}
   345  	hostPort := requiredHTTPPort(req.TLS != nil, p)
   346  	if hostPort > 0 {
   347  		n++
   348  	}
   349  	peer, peerPort := splitHostPort(req.RemoteAddr)
   350  	if peer != "" {
   351  		n++
   352  		if peerPort > 0 {
   353  			n++
   354  		}
   355  	}
   356  	useragent := req.UserAgent()
   357  	if useragent != "" {
   358  		n++
   359  	}
   360  
   361  	clientIP := serverClientIP(req.Header.Get("X-Forwarded-For"))
   362  	if clientIP != "" {
   363  		n++
   364  	}
   365  
   366  	var target string
   367  	if req.URL != nil {
   368  		target = req.URL.Path
   369  		if target != "" {
   370  			n++
   371  		}
   372  	}
   373  	protoName, protoVersion := netProtocol(req.Proto)
   374  	if protoName != "" && protoName != "http" {
   375  		n++
   376  	}
   377  	if protoVersion != "" {
   378  		n++
   379  	}
   380  
   381  	attrs := make([]attribute.KeyValue, 0, n)
   382  
   383  	attrs = append(attrs, c.method(req.Method))
   384  	attrs = append(attrs, c.scheme(req.TLS != nil))
   385  	attrs = append(attrs, c.NetConv.HostName(host))
   386  
   387  	if hostPort > 0 {
   388  		attrs = append(attrs, c.NetConv.HostPort(hostPort))
   389  	}
   390  
   391  	if peer != "" {
   392  		// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
   393  		// file-path that would be interpreted with a sock family.
   394  		attrs = append(attrs, c.NetConv.SockPeerAddr(peer))
   395  		if peerPort > 0 {
   396  			attrs = append(attrs, c.NetConv.SockPeerPort(peerPort))
   397  		}
   398  	}
   399  
   400  	if useragent != "" {
   401  		attrs = append(attrs, c.UserAgentOriginalKey.String(useragent))
   402  	}
   403  
   404  	if clientIP != "" {
   405  		attrs = append(attrs, c.HTTPClientIPKey.String(clientIP))
   406  	}
   407  
   408  	if target != "" {
   409  		attrs = append(attrs, c.HTTPTargetKey.String(target))
   410  	}
   411  
   412  	if protoName != "" && protoName != "http" {
   413  		attrs = append(attrs, c.NetConv.NetProtocolName.String(protoName))
   414  	}
   415  	if protoVersion != "" {
   416  		attrs = append(attrs, c.NetConv.NetProtocolVersion.String(protoVersion))
   417  	}
   418  
   419  	return attrs
   420  }
   421  
   422  // ServerRequestMetrics returns metric attributes for an HTTP request received
   423  // by a server.
   424  //
   425  // The server must be the primary server name if it is known. For example this
   426  // would be the ServerName directive
   427  // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
   428  // server, and the server_name directive
   429  // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
   430  // nginx server. More generically, the primary server name would be the host
   431  // header value that matches the default virtual host of an HTTP server. It
   432  // should include the host identifier and if a port is used to route to the
   433  // server that port identifier should be included as an appropriate port
   434  // suffix.
   435  //
   436  // If the primary server name is not known, server should be an empty string.
   437  // The req Host will be used to determine the server instead.
   438  //
   439  // The following attributes are always returned: "http.method", "http.scheme",
   440  // "net.host.name". The following attributes are returned if they related
   441  // values are defined in req: "net.host.port".
   442  func (c *httpConv) ServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue {
   443  	/* The following semantic conventions are returned if present:
   444  	http.scheme             string
   445  	http.route              string
   446  	http.method             string
   447  	http.status_code        int
   448  	net.host.name           string
   449  	net.host.port           int
   450  	net.protocol.name       string Note: not set if the value is "http".
   451  	net.protocol.version    string
   452  	*/
   453  
   454  	n := 3 // Method, scheme, and host name.
   455  	var host string
   456  	var p int
   457  	if server == "" {
   458  		host, p = splitHostPort(req.Host)
   459  	} else {
   460  		// Prioritize the primary server name.
   461  		host, p = splitHostPort(server)
   462  		if p < 0 {
   463  			_, p = splitHostPort(req.Host)
   464  		}
   465  	}
   466  	hostPort := requiredHTTPPort(req.TLS != nil, p)
   467  	if hostPort > 0 {
   468  		n++
   469  	}
   470  	protoName, protoVersion := netProtocol(req.Proto)
   471  	if protoName != "" {
   472  		n++
   473  	}
   474  	if protoVersion != "" {
   475  		n++
   476  	}
   477  
   478  	attrs := make([]attribute.KeyValue, 0, n)
   479  
   480  	attrs = append(attrs, c.methodMetric(req.Method))
   481  	attrs = append(attrs, c.scheme(req.TLS != nil))
   482  	attrs = append(attrs, c.NetConv.HostName(host))
   483  
   484  	if hostPort > 0 {
   485  		attrs = append(attrs, c.NetConv.HostPort(hostPort))
   486  	}
   487  	if protoName != "" {
   488  		attrs = append(attrs, c.NetConv.NetProtocolName.String(protoName))
   489  	}
   490  	if protoVersion != "" {
   491  		attrs = append(attrs, c.NetConv.NetProtocolVersion.String(protoVersion))
   492  	}
   493  
   494  	return attrs
   495  }
   496  
   497  func (c *httpConv) method(method string) attribute.KeyValue {
   498  	if method == "" {
   499  		return c.HTTPMethodKey.String(http.MethodGet)
   500  	}
   501  	return c.HTTPMethodKey.String(method)
   502  }
   503  
   504  func (c *httpConv) methodMetric(method string) attribute.KeyValue {
   505  	method = strings.ToUpper(method)
   506  	switch method {
   507  	case http.MethodConnect, http.MethodDelete, http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodPatch, http.MethodPost, http.MethodPut, http.MethodTrace:
   508  	default:
   509  		method = "_OTHER"
   510  	}
   511  	return c.HTTPMethodKey.String(method)
   512  }
   513  
   514  func (c *httpConv) scheme(https bool) attribute.KeyValue { // nolint:revive
   515  	if https {
   516  		return c.HTTPSchemeHTTPS
   517  	}
   518  	return c.HTTPSchemeHTTP
   519  }
   520  
   521  func serverClientIP(xForwardedFor string) string {
   522  	if idx := strings.Index(xForwardedFor, ","); idx >= 0 {
   523  		xForwardedFor = xForwardedFor[:idx]
   524  	}
   525  	return xForwardedFor
   526  }
   527  
   528  func requiredHTTPPort(https bool, port int) int { // nolint:revive
   529  	if https {
   530  		if port > 0 && port != 443 {
   531  			return port
   532  		}
   533  	} else {
   534  		if port > 0 && port != 80 {
   535  			return port
   536  		}
   537  	}
   538  	return -1
   539  }
   540  
   541  // Return the request host and port from the first non-empty source.
   542  func firstHostPort(source ...string) (host string, port int) {
   543  	for _, hostport := range source {
   544  		host, port = splitHostPort(hostport)
   545  		if host != "" || port > 0 {
   546  			break
   547  		}
   548  	}
   549  	return
   550  }
   551  
   552  // ClientStatus returns a span status code and message for an HTTP status code
   553  // value received by a client.
   554  func (c *httpConv) ClientStatus(code int) (codes.Code, string) {
   555  	if code < 100 || code >= 600 {
   556  		return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
   557  	}
   558  	if code >= 400 {
   559  		return codes.Error, ""
   560  	}
   561  	return codes.Unset, ""
   562  }
   563  
   564  // ServerStatus returns a span status code and message for an HTTP status code
   565  // value returned by a server. Status codes in the 400-499 range are not
   566  // returned as errors.
   567  func (c *httpConv) ServerStatus(code int) (codes.Code, string) {
   568  	if code < 100 || code >= 600 {
   569  		return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
   570  	}
   571  	if code >= 500 {
   572  		return codes.Error, ""
   573  	}
   574  	return codes.Unset, ""
   575  }
   576  

View as plain text