From d6cdffb69ee7bba040ea3c3533bf83220caa9813 Mon Sep 17 00:00:00 2001 From: Jason Parraga Date: Sun, 17 Dec 2023 23:50:29 -0800 Subject: [PATCH] make precommit --- .../internal/semconvutil/httpconv.go | 40 +++++++++++++ .../internal/semconvutil/httpconv_test.go | 56 ++++++++++++++++++ .../otelgin/internal/semconvutil/httpconv.go | 40 +++++++++++++ .../internal/semconvutil/httpconv_test.go | 56 ++++++++++++++++++ .../otelmux/internal/semconvutil/httpconv.go | 40 +++++++++++++ .../internal/semconvutil/httpconv_test.go | 56 ++++++++++++++++++ .../otelecho/internal/semconvutil/httpconv.go | 40 +++++++++++++ .../internal/semconvutil/httpconv_test.go | 56 ++++++++++++++++++ .../internal/semconvutil/httpconv.go | 40 +++++++++++++ .../internal/semconvutil/httpconv_test.go | 56 ++++++++++++++++++ .../httptrace/otelhttptrace/example/go.sum | 1 + .../net/http/httptrace/otelhttptrace/go.sum | 3 + .../internal/semconvutil/httpconv.go | 40 +++++++++++++ .../internal/semconvutil/httpconv_test.go | 56 ++++++++++++++++++ instrumentation/net/http/otelhttp/go.mod | 2 +- .../otelhttp/internal/semconvutil/httpconv.go | 5 +- .../internal/semconvutil/httpconv_test.go | 1 - .../net/http/otelhttp/transport.go | 3 +- .../net/http/otelhttp/transport_test.go | 14 ++--- internal/shared/semconvutil/httpconv.go.tmpl | 40 +++++++++++++ .../shared/semconvutil/httpconv_test.go.tmpl | 58 +++++++++++++++++++ 21 files changed, 688 insertions(+), 15 deletions(-) diff --git a/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv.go b/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv.go index 4a184fec736..0394cadcfbf 100644 --- a/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv.go +++ b/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv.go @@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue { return hc.ClientRequest(req) } +// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client. +// The following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the +// related values are defined in req: "net.peer.port". +func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue { + return hc.ClientRequestMetrics(req) +} + // HTTPClientStatus returns a span status code and message for an HTTP status code // value received by a client. func HTTPClientStatus(code int) (codes.Code, string) { @@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue { return attrs } +// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The +// following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the related values +// are defined in req: "net.peer.port". +func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue { + /* The following semantic conventions are returned if present: + http.method string + net.peer.name string + net.peer.port int + */ + + n := 2 // method, peer name. + var h string + if req.URL != nil { + h = req.URL.Host + } + peer, p := firstHostPort(h, req.Header.Get("Host")) + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p) + if port > 0 { + n++ + } + + attrs := make([]attribute.KeyValue, 0, n) + attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer)) + + if port > 0 { + attrs = append(attrs, c.NetConv.PeerPort(port)) + } + + return attrs +} + // ServerRequest returns attributes for an HTTP request received by a server. // // The server must be the primary server name if it is known. For example this diff --git a/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv_test.go b/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv_test.go index d36fe489667..26a2fe8cfbf 100644 --- a/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv_test.go +++ b/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv_test.go @@ -67,6 +67,28 @@ func TestHTTPSClientRequest(t *testing.T) { HTTPClientRequest(req), ) } +func TestHTTPSClientRequestMetrics(t *testing.T) { + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "https", + Host: "127.0.0.1:443", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + } + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + }, + HTTPClientRequestMetrics(req), + ) +} func TestHTTPClientRequest(t *testing.T) { const ( @@ -105,6 +127,40 @@ func TestHTTPClientRequest(t *testing.T) { ) } +func TestHTTPClientRequestMetrics(t *testing.T) { + const ( + user = "alice" + n = 128 + agent = "Go-http-client/1.1" + ) + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "http", + Host: "127.0.0.1:8080", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Header: http.Header{ + "User-Agent": []string{agent}, + }, + ContentLength: n, + } + req.SetBasicAuth(user, "pswrd") + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + attribute.Int("net.peer.port", 8080), + }, + HTTPClientRequestMetrics(req), + ) +} + func TestHTTPClientRequestRequired(t *testing.T) { req := new(http.Request) var got []attribute.KeyValue diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv.go b/instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv.go index 6ee1dc536a1..bfaf7b6dca1 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv.go @@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue { return hc.ClientRequest(req) } +// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client. +// The following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the +// related values are defined in req: "net.peer.port". +func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue { + return hc.ClientRequestMetrics(req) +} + // HTTPClientStatus returns a span status code and message for an HTTP status code // value received by a client. func HTTPClientStatus(code int) (codes.Code, string) { @@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue { return attrs } +// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The +// following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the related values +// are defined in req: "net.peer.port". +func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue { + /* The following semantic conventions are returned if present: + http.method string + net.peer.name string + net.peer.port int + */ + + n := 2 // method, peer name. + var h string + if req.URL != nil { + h = req.URL.Host + } + peer, p := firstHostPort(h, req.Header.Get("Host")) + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p) + if port > 0 { + n++ + } + + attrs := make([]attribute.KeyValue, 0, n) + attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer)) + + if port > 0 { + attrs = append(attrs, c.NetConv.PeerPort(port)) + } + + return attrs +} + // ServerRequest returns attributes for an HTTP request received by a server. // // The server must be the primary server name if it is known. For example this diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv_test.go b/instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv_test.go index d36fe489667..26a2fe8cfbf 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv_test.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv_test.go @@ -67,6 +67,28 @@ func TestHTTPSClientRequest(t *testing.T) { HTTPClientRequest(req), ) } +func TestHTTPSClientRequestMetrics(t *testing.T) { + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "https", + Host: "127.0.0.1:443", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + } + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + }, + HTTPClientRequestMetrics(req), + ) +} func TestHTTPClientRequest(t *testing.T) { const ( @@ -105,6 +127,40 @@ func TestHTTPClientRequest(t *testing.T) { ) } +func TestHTTPClientRequestMetrics(t *testing.T) { + const ( + user = "alice" + n = 128 + agent = "Go-http-client/1.1" + ) + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "http", + Host: "127.0.0.1:8080", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Header: http.Header{ + "User-Agent": []string{agent}, + }, + ContentLength: n, + } + req.SetBasicAuth(user, "pswrd") + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + attribute.Int("net.peer.port", 8080), + }, + HTTPClientRequestMetrics(req), + ) +} + func TestHTTPClientRequestRequired(t *testing.T) { req := new(http.Request) var got []attribute.KeyValue diff --git a/instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv.go b/instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv.go index 79bf47e7b76..62a7e62e32d 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv.go +++ b/instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv.go @@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue { return hc.ClientRequest(req) } +// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client. +// The following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the +// related values are defined in req: "net.peer.port". +func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue { + return hc.ClientRequestMetrics(req) +} + // HTTPClientStatus returns a span status code and message for an HTTP status code // value received by a client. func HTTPClientStatus(code int) (codes.Code, string) { @@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue { return attrs } +// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The +// following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the related values +// are defined in req: "net.peer.port". +func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue { + /* The following semantic conventions are returned if present: + http.method string + net.peer.name string + net.peer.port int + */ + + n := 2 // method, peer name. + var h string + if req.URL != nil { + h = req.URL.Host + } + peer, p := firstHostPort(h, req.Header.Get("Host")) + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p) + if port > 0 { + n++ + } + + attrs := make([]attribute.KeyValue, 0, n) + attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer)) + + if port > 0 { + attrs = append(attrs, c.NetConv.PeerPort(port)) + } + + return attrs +} + // ServerRequest returns attributes for an HTTP request received by a server. // // The server must be the primary server name if it is known. For example this diff --git a/instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv_test.go b/instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv_test.go index d36fe489667..26a2fe8cfbf 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv_test.go +++ b/instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv_test.go @@ -67,6 +67,28 @@ func TestHTTPSClientRequest(t *testing.T) { HTTPClientRequest(req), ) } +func TestHTTPSClientRequestMetrics(t *testing.T) { + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "https", + Host: "127.0.0.1:443", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + } + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + }, + HTTPClientRequestMetrics(req), + ) +} func TestHTTPClientRequest(t *testing.T) { const ( @@ -105,6 +127,40 @@ func TestHTTPClientRequest(t *testing.T) { ) } +func TestHTTPClientRequestMetrics(t *testing.T) { + const ( + user = "alice" + n = 128 + agent = "Go-http-client/1.1" + ) + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "http", + Host: "127.0.0.1:8080", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Header: http.Header{ + "User-Agent": []string{agent}, + }, + ContentLength: n, + } + req.SetBasicAuth(user, "pswrd") + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + attribute.Int("net.peer.port", 8080), + }, + HTTPClientRequestMetrics(req), + ) +} + func TestHTTPClientRequestRequired(t *testing.T) { req := new(http.Request) var got []attribute.KeyValue diff --git a/instrumentation/github.com/labstack/echo/otelecho/internal/semconvutil/httpconv.go b/instrumentation/github.com/labstack/echo/otelecho/internal/semconvutil/httpconv.go index f2b2b86dafe..0ad79ce6a8b 100644 --- a/instrumentation/github.com/labstack/echo/otelecho/internal/semconvutil/httpconv.go +++ b/instrumentation/github.com/labstack/echo/otelecho/internal/semconvutil/httpconv.go @@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue { return hc.ClientRequest(req) } +// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client. +// The following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the +// related values are defined in req: "net.peer.port". +func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue { + return hc.ClientRequestMetrics(req) +} + // HTTPClientStatus returns a span status code and message for an HTTP status code // value received by a client. func HTTPClientStatus(code int) (codes.Code, string) { @@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue { return attrs } +// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The +// following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the related values +// are defined in req: "net.peer.port". +func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue { + /* The following semantic conventions are returned if present: + http.method string + net.peer.name string + net.peer.port int + */ + + n := 2 // method, peer name. + var h string + if req.URL != nil { + h = req.URL.Host + } + peer, p := firstHostPort(h, req.Header.Get("Host")) + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p) + if port > 0 { + n++ + } + + attrs := make([]attribute.KeyValue, 0, n) + attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer)) + + if port > 0 { + attrs = append(attrs, c.NetConv.PeerPort(port)) + } + + return attrs +} + // ServerRequest returns attributes for an HTTP request received by a server. // // The server must be the primary server name if it is known. For example this diff --git a/instrumentation/github.com/labstack/echo/otelecho/internal/semconvutil/httpconv_test.go b/instrumentation/github.com/labstack/echo/otelecho/internal/semconvutil/httpconv_test.go index d36fe489667..26a2fe8cfbf 100644 --- a/instrumentation/github.com/labstack/echo/otelecho/internal/semconvutil/httpconv_test.go +++ b/instrumentation/github.com/labstack/echo/otelecho/internal/semconvutil/httpconv_test.go @@ -67,6 +67,28 @@ func TestHTTPSClientRequest(t *testing.T) { HTTPClientRequest(req), ) } +func TestHTTPSClientRequestMetrics(t *testing.T) { + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "https", + Host: "127.0.0.1:443", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + } + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + }, + HTTPClientRequestMetrics(req), + ) +} func TestHTTPClientRequest(t *testing.T) { const ( @@ -105,6 +127,40 @@ func TestHTTPClientRequest(t *testing.T) { ) } +func TestHTTPClientRequestMetrics(t *testing.T) { + const ( + user = "alice" + n = 128 + agent = "Go-http-client/1.1" + ) + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "http", + Host: "127.0.0.1:8080", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Header: http.Header{ + "User-Agent": []string{agent}, + }, + ContentLength: n, + } + req.SetBasicAuth(user, "pswrd") + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + attribute.Int("net.peer.port", 8080), + }, + HTTPClientRequestMetrics(req), + ) +} + func TestHTTPClientRequestRequired(t *testing.T) { req := new(http.Request) var got []attribute.KeyValue diff --git a/instrumentation/gopkg.in/macaron.v1/otelmacaron/internal/semconvutil/httpconv.go b/instrumentation/gopkg.in/macaron.v1/otelmacaron/internal/semconvutil/httpconv.go index 53a85e74fba..9e8cfe84d14 100644 --- a/instrumentation/gopkg.in/macaron.v1/otelmacaron/internal/semconvutil/httpconv.go +++ b/instrumentation/gopkg.in/macaron.v1/otelmacaron/internal/semconvutil/httpconv.go @@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue { return hc.ClientRequest(req) } +// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client. +// The following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the +// related values are defined in req: "net.peer.port". +func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue { + return hc.ClientRequestMetrics(req) +} + // HTTPClientStatus returns a span status code and message for an HTTP status code // value received by a client. func HTTPClientStatus(code int) (codes.Code, string) { @@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue { return attrs } +// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The +// following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the related values +// are defined in req: "net.peer.port". +func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue { + /* The following semantic conventions are returned if present: + http.method string + net.peer.name string + net.peer.port int + */ + + n := 2 // method, peer name. + var h string + if req.URL != nil { + h = req.URL.Host + } + peer, p := firstHostPort(h, req.Header.Get("Host")) + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p) + if port > 0 { + n++ + } + + attrs := make([]attribute.KeyValue, 0, n) + attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer)) + + if port > 0 { + attrs = append(attrs, c.NetConv.PeerPort(port)) + } + + return attrs +} + // ServerRequest returns attributes for an HTTP request received by a server. // // The server must be the primary server name if it is known. For example this diff --git a/instrumentation/gopkg.in/macaron.v1/otelmacaron/internal/semconvutil/httpconv_test.go b/instrumentation/gopkg.in/macaron.v1/otelmacaron/internal/semconvutil/httpconv_test.go index d36fe489667..26a2fe8cfbf 100644 --- a/instrumentation/gopkg.in/macaron.v1/otelmacaron/internal/semconvutil/httpconv_test.go +++ b/instrumentation/gopkg.in/macaron.v1/otelmacaron/internal/semconvutil/httpconv_test.go @@ -67,6 +67,28 @@ func TestHTTPSClientRequest(t *testing.T) { HTTPClientRequest(req), ) } +func TestHTTPSClientRequestMetrics(t *testing.T) { + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "https", + Host: "127.0.0.1:443", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + } + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + }, + HTTPClientRequestMetrics(req), + ) +} func TestHTTPClientRequest(t *testing.T) { const ( @@ -105,6 +127,40 @@ func TestHTTPClientRequest(t *testing.T) { ) } +func TestHTTPClientRequestMetrics(t *testing.T) { + const ( + user = "alice" + n = 128 + agent = "Go-http-client/1.1" + ) + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "http", + Host: "127.0.0.1:8080", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Header: http.Header{ + "User-Agent": []string{agent}, + }, + ContentLength: n, + } + req.SetBasicAuth(user, "pswrd") + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + attribute.Int("net.peer.port", 8080), + }, + HTTPClientRequestMetrics(req), + ) +} + func TestHTTPClientRequestRequired(t *testing.T) { req := new(http.Request) var got []attribute.KeyValue diff --git a/instrumentation/net/http/httptrace/otelhttptrace/example/go.sum b/instrumentation/net/http/httptrace/otelhttptrace/example/go.sum index 258484c5f54..97697fdc24b 100644 --- a/instrumentation/net/http/httptrace/otelhttptrace/example/go.sum +++ b/instrumentation/net/http/httptrace/otelhttptrace/example/go.sum @@ -17,6 +17,7 @@ go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ3 go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= diff --git a/instrumentation/net/http/httptrace/otelhttptrace/go.sum b/instrumentation/net/http/httptrace/otelhttptrace/go.sum index 8795d94621f..efa742e850d 100644 --- a/instrumentation/net/http/httptrace/otelhttptrace/go.sum +++ b/instrumentation/net/http/httptrace/otelhttptrace/go.sum @@ -17,8 +17,11 @@ go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil/httpconv.go b/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil/httpconv.go index 1049ed94082..b5a0831f35c 100644 --- a/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil/httpconv.go +++ b/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil/httpconv.go @@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue { return hc.ClientRequest(req) } +// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client. +// The following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the +// related values are defined in req: "net.peer.port". +func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue { + return hc.ClientRequestMetrics(req) +} + // HTTPClientStatus returns a span status code and message for an HTTP status code // value received by a client. func HTTPClientStatus(code int) (codes.Code, string) { @@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue { return attrs } +// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The +// following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the related values +// are defined in req: "net.peer.port". +func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue { + /* The following semantic conventions are returned if present: + http.method string + net.peer.name string + net.peer.port int + */ + + n := 2 // method, peer name. + var h string + if req.URL != nil { + h = req.URL.Host + } + peer, p := firstHostPort(h, req.Header.Get("Host")) + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p) + if port > 0 { + n++ + } + + attrs := make([]attribute.KeyValue, 0, n) + attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer)) + + if port > 0 { + attrs = append(attrs, c.NetConv.PeerPort(port)) + } + + return attrs +} + // ServerRequest returns attributes for an HTTP request received by a server. // // The server must be the primary server name if it is known. For example this diff --git a/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil/httpconv_test.go b/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil/httpconv_test.go index d36fe489667..26a2fe8cfbf 100644 --- a/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil/httpconv_test.go +++ b/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil/httpconv_test.go @@ -67,6 +67,28 @@ func TestHTTPSClientRequest(t *testing.T) { HTTPClientRequest(req), ) } +func TestHTTPSClientRequestMetrics(t *testing.T) { + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "https", + Host: "127.0.0.1:443", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + } + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + }, + HTTPClientRequestMetrics(req), + ) +} func TestHTTPClientRequest(t *testing.T) { const ( @@ -105,6 +127,40 @@ func TestHTTPClientRequest(t *testing.T) { ) } +func TestHTTPClientRequestMetrics(t *testing.T) { + const ( + user = "alice" + n = 128 + agent = "Go-http-client/1.1" + ) + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "http", + Host: "127.0.0.1:8080", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Header: http.Header{ + "User-Agent": []string{agent}, + }, + ContentLength: n, + } + req.SetBasicAuth(user, "pswrd") + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + attribute.Int("net.peer.port", 8080), + }, + HTTPClientRequestMetrics(req), + ) +} + func TestHTTPClientRequestRequired(t *testing.T) { req := new(http.Request) var got []attribute.KeyValue diff --git a/instrumentation/net/http/otelhttp/go.mod b/instrumentation/net/http/otelhttp/go.mod index 875e169bfb5..0bbb46ffa75 100644 --- a/instrumentation/net/http/otelhttp/go.mod +++ b/instrumentation/net/http/otelhttp/go.mod @@ -7,6 +7,7 @@ require ( github.com/stretchr/testify v1.8.4 go.opentelemetry.io/otel v1.21.0 go.opentelemetry.io/otel/metric v1.21.0 + go.opentelemetry.io/otel/sdk v1.21.0 go.opentelemetry.io/otel/sdk/metric v1.21.0 go.opentelemetry.io/otel/trace v1.21.0 ) @@ -16,7 +17,6 @@ require ( github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.opentelemetry.io/otel/sdk v1.21.0 // indirect golang.org/x/sys v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go b/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go index 8694b17ec57..f4e496bf4bd 100644 --- a/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go +++ b/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go @@ -317,8 +317,7 @@ func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue } attrs := make([]attribute.KeyValue, 0, n) - attrs = append(attrs, c.method(req.Method)) - attrs = append(attrs, c.NetConv.PeerName(peer)) + attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer)) if port > 0 { attrs = append(attrs, c.NetConv.PeerPort(port)) @@ -481,7 +480,7 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K // // The following attributes are always returned: "http.method", "http.scheme", // "http.flavor", "net.host.name". The following attributes are -// returned if the related values are defined in req: "net.host.port". +// returned if they related values are defined in req: "net.host.port". func (c *httpConv) ServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue { /* The following semantic conventions are returned if present: http.scheme string diff --git a/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv_test.go b/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv_test.go index ea59b0706ec..26a2fe8cfbf 100644 --- a/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv_test.go +++ b/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv_test.go @@ -67,7 +67,6 @@ func TestHTTPSClientRequest(t *testing.T) { HTTPClientRequest(req), ) } - func TestHTTPSClientRequestMetrics(t *testing.T) { req := &http.Request{ Method: http.MethodGet, diff --git a/instrumentation/net/http/otelhttp/transport.go b/instrumentation/net/http/otelhttp/transport.go index fd596f6ae05..9c2eff43612 100644 --- a/instrumentation/net/http/otelhttp/transport.go +++ b/instrumentation/net/http/otelhttp/transport.go @@ -16,13 +16,14 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http import ( "context" - "go.opentelemetry.io/otel/metric" "io" "net/http" "net/http/httptrace" "sync/atomic" "time" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" diff --git a/instrumentation/net/http/otelhttp/transport_test.go b/instrumentation/net/http/otelhttp/transport_test.go index a01eb3fb4cd..fb66a0d896d 100644 --- a/instrumentation/net/http/otelhttp/transport_test.go +++ b/instrumentation/net/http/otelhttp/transport_test.go @@ -18,11 +18,6 @@ import ( "bytes" "context" "errors" - "fmt" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/sdk/instrumentation" - "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" - semconv "go.opentelemetry.io/otel/semconv/v1.20.0" "io" "net" "net/http" @@ -31,6 +26,11 @@ import ( "strings" "testing" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" + semconv "go.opentelemetry.io/otel/semconv/v1.20.0" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -505,7 +505,6 @@ func TestTransportMetrics(t *testing.T) { } func assertClientScopeMetrics(t *testing.T, sm metricdata.ScopeMetrics, attrs attribute.Set) { - assert.Equal(t, instrumentation.Scope{ Name: "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp", Version: Version(), @@ -548,8 +547,6 @@ func assertClientScopeMetrics(t *testing.T, sm metricdata.ScopeMetrics, attrs at assert.Equal(t, uint64(1), dPt.Count, "count") assert.Equal(t, []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}, dPt.Bounds, "bounds") - t.Log(fmt.Sprintf("%+v", dPt.BucketCounts)) - // Duration value is not deterministic because the code does not support a pluggable clock. // So just value that one of the buckets has been incremented. bucketSum := uint64(0) @@ -558,5 +555,4 @@ func assertClientScopeMetrics(t *testing.T, sm metricdata.ScopeMetrics, attrs at } require.Equal(t, uint64(1), bucketSum) - } diff --git a/internal/shared/semconvutil/httpconv.go.tmpl b/internal/shared/semconvutil/httpconv.go.tmpl index 8f299896ccb..86ade834224 100644 --- a/internal/shared/semconvutil/httpconv.go.tmpl +++ b/internal/shared/semconvutil/httpconv.go.tmpl @@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue { return hc.ClientRequest(req) } +// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client. +// The following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the +// related values are defined in req: "net.peer.port". +func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue { + return hc.ClientRequestMetrics(req) +} + // HTTPClientStatus returns a span status code and message for an HTTP status code // value received by a client. func HTTPClientStatus(code int) (codes.Code, string) { @@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue { return attrs } +// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The +// following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the related values +// are defined in req: "net.peer.port". +func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue { + /* The following semantic conventions are returned if present: + http.method string + net.peer.name string + net.peer.port int + */ + + n := 2 // method, peer name. + var h string + if req.URL != nil { + h = req.URL.Host + } + peer, p := firstHostPort(h, req.Header.Get("Host")) + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p) + if port > 0 { + n++ + } + + attrs := make([]attribute.KeyValue, 0, n) + attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer)) + + if port > 0 { + attrs = append(attrs, c.NetConv.PeerPort(port)) + } + + return attrs +} + // ServerRequest returns attributes for an HTTP request received by a server. // // The server must be the primary server name if it is known. For example this diff --git a/internal/shared/semconvutil/httpconv_test.go.tmpl b/internal/shared/semconvutil/httpconv_test.go.tmpl index d36fe489667..a44240eb060 100644 --- a/internal/shared/semconvutil/httpconv_test.go.tmpl +++ b/internal/shared/semconvutil/httpconv_test.go.tmpl @@ -68,6 +68,30 @@ func TestHTTPSClientRequest(t *testing.T) { ) } + +func TestHTTPSClientRequestMetrics(t *testing.T) { + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "https", + Host: "127.0.0.1:443", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + } + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + }, + HTTPClientRequestMetrics(req), + ) +} + func TestHTTPClientRequest(t *testing.T) { const ( user = "alice" @@ -105,6 +129,40 @@ func TestHTTPClientRequest(t *testing.T) { ) } +func TestHTTPClientRequestMetrics(t *testing.T) { + const ( + user = "alice" + n = 128 + agent = "Go-http-client/1.1" + ) + req := &http.Request{ + Method: http.MethodGet, + URL: &url.URL{ + Scheme: "http", + Host: "127.0.0.1:8080", + Path: "/resource", + }, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Header: http.Header{ + "User-Agent": []string{agent}, + }, + ContentLength: n, + } + req.SetBasicAuth(user, "pswrd") + + assert.ElementsMatch( + t, + []attribute.KeyValue{ + attribute.String("http.method", "GET"), + attribute.String("net.peer.name", "127.0.0.1"), + attribute.Int("net.peer.port", 8080), + }, + HTTPClientRequestMetrics(req), + ) +} + func TestHTTPClientRequestRequired(t *testing.T) { req := new(http.Request) var got []attribute.KeyValue