Skip to content

Commit

Permalink
Update http Semconv to v1.20.0 (#4320)
Browse files Browse the repository at this point in the history
* update client & server request

* client resp and server metrics

* Update template.

* update http instrumentation to 1.20

* Update changelog

* Address PR feedback

* Update comments about protocol.name

* Apply suggestions from code review

---------

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
  • Loading branch information
MadVikingGod and MrAlias committed Dec 12, 2023
1 parent f7ca3ec commit 74f4b7f
Show file tree
Hide file tree
Showing 47 changed files with 1,295 additions and 785 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Upgrade dependencies of OpenTelemetry Go to use the new [`v1.19.0`/`v0.42.0`/`v0.0.7` release](https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.19.0).
- Use `grpc.StatsHandler` for gRPC instrumentation in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/example`. (#4325)

### Changed

- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful` have been upgraded to v1.20.0. (#4320)
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin` have been upgraded to v1.20.0. (#4320)
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux` have been upgraded to v1.20.0. (#4320)
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho` have been upgraded to v1.20.0. (#4320)
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/gopkg.in/macaron.v1/otelmacaron` have been upgraded to v1.20.0. (#4320)
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace` have been upgraded to v1.20.0. (#4320)
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/example` have been upgraded to v1.20.0. (#4320)
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/example` have been upgraded to v1.20.0. (#4320)
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` have been upgraded to v1.20.0. (#4320)

## [1.19.0/0.44.0/0.13.0] - 2023-09-12

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
)

// HTTPClientResponse returns trace attributes for an HTTP response received by a
Expand Down Expand Up @@ -149,9 +149,7 @@ func HTTPResponseHeader(h http.Header) []attribute.KeyValue {
type httpConv struct {
NetConv *netConv

EnduserIDKey attribute.Key
HTTPClientIPKey attribute.Key
HTTPFlavorKey attribute.Key
HTTPMethodKey attribute.Key
HTTPRequestContentLengthKey attribute.Key
HTTPResponseContentLengthKey attribute.Key
Expand All @@ -161,15 +159,13 @@ type httpConv struct {
HTTPStatusCodeKey attribute.Key
HTTPTargetKey attribute.Key
HTTPURLKey attribute.Key
HTTPUserAgentKey attribute.Key
UserAgentOriginalKey attribute.Key
}

var hc = &httpConv{
NetConv: nc,

EnduserIDKey: semconv.EnduserIDKey,
HTTPClientIPKey: semconv.HTTPClientIPKey,
HTTPFlavorKey: semconv.HTTPFlavorKey,
HTTPMethodKey: semconv.HTTPMethodKey,
HTTPRequestContentLengthKey: semconv.HTTPRequestContentLengthKey,
HTTPResponseContentLengthKey: semconv.HTTPResponseContentLengthKey,
Expand All @@ -179,7 +175,7 @@ var hc = &httpConv{
HTTPStatusCodeKey: semconv.HTTPStatusCodeKey,
HTTPTargetKey: semconv.HTTPTargetKey,
HTTPURLKey: semconv.HTTPURLKey,
HTTPUserAgentKey: semconv.HTTPUserAgentKey,
UserAgentOriginalKey: semconv.UserAgentOriginalKey,
}

// ClientResponse returns attributes for an HTTP response received by a client
Expand All @@ -193,6 +189,10 @@ var hc = &httpConv{
//
// append(ClientResponse(resp), ClientRequest(resp.Request)...)
func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
/* The following semantic conventions are returned if present:
http.status_code int
http.response_content_length int
*/
var n int
if resp.StatusCode > 0 {
n++
Expand All @@ -212,11 +212,31 @@ func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
}

// ClientRequest returns attributes for an HTTP request made by a client. The
// following attributes are always returned: "http.url", "http.flavor",
// "http.method", "net.peer.name". The following attributes are returned if the
// related values are defined in req: "net.peer.port", "http.user_agent",
// "http.request_content_length", "enduser.id".
// following attributes are always returned: "http.url", "http.method",
// "net.peer.name". The following attributes are returned if the related values
// are defined in req: "net.peer.port", "http.user_agent",
// "http.request_content_length", "user_agent.original".
func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
/* The following semantic conventions are returned if present:
http.method string
user_agent.original string
http.url string
net.peer.name string
net.peer.port int
http.request_content_length int
*/

/* The following semantic conventions are not returned:
http.status_code This requires the response. See ClientResponse.
http.response_content_length This requires the response. See ClientResponse.
net.sock.family This requires the socket used.
net.sock.peer.addr This requires the socket used.
net.sock.peer.name This requires the socket used.
net.sock.peer.port This requires the socket used.
http.resend_count This is something outside of a single request.
net.protocol.name The value is the Request is ignored, and the go client will always use "http".
net.protocol.version The value in the Request is ignored, and the go client will always use 1.1 or 2.0.
*/
n := 3 // URL, peer name, proto, and method.
var h string
if req.URL != nil {
Expand All @@ -234,14 +254,10 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
if req.ContentLength > 0 {
n++
}
userID, _, hasUserID := req.BasicAuth()
if hasUserID {
n++
}

attrs := make([]attribute.KeyValue, 0, n)

attrs = append(attrs, c.method(req.Method))
attrs = append(attrs, c.flavor(req.Proto))

var u string
if req.URL != nil {
Expand All @@ -260,17 +276,13 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
}

if useragent != "" {
attrs = append(attrs, c.HTTPUserAgentKey.String(useragent))
attrs = append(attrs, c.UserAgentOriginalKey.String(useragent))
}

if l := req.ContentLength; l > 0 {
attrs = append(attrs, c.HTTPRequestContentLengthKey.Int64(l))
}

if hasUserID {
attrs = append(attrs, c.EnduserIDKey.String(userID))
}

return attrs
}

Expand All @@ -291,18 +303,35 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
// The req Host will be used to determine the server instead.
//
// The following attributes are always returned: "http.method", "http.scheme",
// "http.flavor", "http.target", "net.host.name". The following attributes are
// returned if they related values are defined in req: "net.host.port",
// "net.sock.peer.addr", "net.sock.peer.port", "http.user_agent", "enduser.id",
// "http.client_ip".
// "http.target", "net.host.name". The following attributes are returned if they
// related values are defined in req: "net.host.port", "net.sock.peer.addr",
// "net.sock.peer.port", "user_agent.original", "http.client_ip",
// "net.protocol.name", "net.protocol.version".
func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.KeyValue {
// TODO: This currently does not add the specification required
// `http.target` attribute. It has too high of a cardinality to safely be
// added. An alternate should be added, or this comment removed, when it is
// addressed by the specification. If it is ultimately decided to continue
// not including the attribute, the HTTPTargetKey field of the httpConv
// should be removed as well.

/* The following semantic conventions are returned if present:
http.method string
http.scheme string
net.host.name string
net.host.port int
net.sock.peer.addr string
net.sock.peer.port int
user_agent.original string
http.client_ip string
net.protocol.name string Note: not set if the value is "http".
net.protocol.version string
http.target string Note: doesn't include the query parameter.
*/

/* The following semantic conventions are not returned:
http.status_code This requires the response.
http.request_content_length This requires the len() of body, which can mutate it.
http.response_content_length This requires the response.
http.route This is not available.
net.sock.peer.name This would require a DNS lookup.
net.sock.host.addr The request doesn't have access to the underlying socket.
net.sock.host.port The request doesn't have access to the underlying socket.
*/
n := 4 // Method, scheme, proto, and host name.
var host string
var p int
Expand Down Expand Up @@ -330,19 +359,31 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K
if useragent != "" {
n++
}
userID, _, hasUserID := req.BasicAuth()
if hasUserID {
n++
}

clientIP := serverClientIP(req.Header.Get("X-Forwarded-For"))
if clientIP != "" {
n++
}

var target string
if req.URL != nil {
target = req.URL.Path
if target != "" {
n++
}
}
protoName, protoVersion := netProtocol(req.Proto)
if protoName != "" && protoName != "http" {
n++
}
if protoVersion != "" {
n++
}

attrs := make([]attribute.KeyValue, 0, n)

attrs = append(attrs, c.method(req.Method))
attrs = append(attrs, c.scheme(req.TLS != nil))
attrs = append(attrs, c.flavor(req.Proto))
attrs = append(attrs, c.NetConv.HostName(host))

if hostPort > 0 {
Expand All @@ -359,17 +400,24 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K
}

if useragent != "" {
attrs = append(attrs, c.HTTPUserAgentKey.String(useragent))
}

if hasUserID {
attrs = append(attrs, c.EnduserIDKey.String(userID))
attrs = append(attrs, c.UserAgentOriginalKey.String(useragent))
}

if clientIP != "" {
attrs = append(attrs, c.HTTPClientIPKey.String(clientIP))
}

if target != "" {
attrs = append(attrs, c.HTTPTargetKey.String(target))
}

if protoName != "" && protoName != "http" {
attrs = append(attrs, c.NetConv.NetProtocolName.String(protoName))
}
if protoVersion != "" {
attrs = append(attrs, c.NetConv.NetProtocolVersion.String(protoVersion))
}

return attrs
}

Expand All @@ -394,14 +442,18 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K
// "http.flavor", "net.host.name". The following attributes are
// returned if they related values are defined in req: "net.host.port".
func (c *httpConv) ServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue {
// TODO: This currently does not add the specification required
// `http.target` attribute. It has too high of a cardinality to safely be
// added. An alternate should be added, or this comment removed, when it is
// addressed by the specification. If it is ultimately decided to continue
// not including the attribute, the HTTPTargetKey field of the httpConv
// should be removed as well.

n := 4 // Method, scheme, proto, and host name.
/* The following semantic conventions are returned if present:
http.scheme string
http.route string
http.method string
http.status_code int
net.host.name string
net.host.port int
net.protocol.name string Note: not set if the value is "http".
net.protocol.version string
*/

n := 3 // Method, scheme, and host name.
var host string
var p int
if server == "" {
Expand All @@ -417,16 +469,29 @@ func (c *httpConv) ServerRequestMetrics(server string, req *http.Request) []attr
if hostPort > 0 {
n++
}
protoName, protoVersion := netProtocol(req.Proto)
if protoName != "" {
n++
}
if protoVersion != "" {
n++
}

attrs := make([]attribute.KeyValue, 0, n)

attrs = append(attrs, c.methodMetric(req.Method))
attrs = append(attrs, c.scheme(req.TLS != nil))
attrs = append(attrs, c.flavor(req.Proto))
attrs = append(attrs, c.NetConv.HostName(host))

if hostPort > 0 {
attrs = append(attrs, c.NetConv.HostPort(hostPort))
}
if protoName != "" {
attrs = append(attrs, c.NetConv.NetProtocolName.String(protoName))
}
if protoVersion != "" {
attrs = append(attrs, c.NetConv.NetProtocolVersion.String(protoVersion))
}

return attrs
}
Expand Down Expand Up @@ -455,21 +520,6 @@ func (c *httpConv) scheme(https bool) attribute.KeyValue { // nolint:revive
return c.HTTPSchemeHTTP
}

func (c *httpConv) flavor(proto string) attribute.KeyValue {
switch proto {
case "HTTP/1.0":
return c.HTTPFlavorKey.String("1.0")
case "HTTP/1.1":
return c.HTTPFlavorKey.String("1.1")
case "HTTP/2":
return c.HTTPFlavorKey.String("2.0")
case "HTTP/3":
return c.HTTPFlavorKey.String("3.0")
default:
return c.HTTPFlavorKey.String(proto)
}
}

func serverClientIP(xForwardedFor string) string {
if idx := strings.Index(xForwardedFor, ","); idx >= 0 {
xForwardedFor = xForwardedFor[:idx]
Expand Down

0 comments on commit 74f4b7f

Please sign in to comment.