Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

otelhttp: client metrics #4707

Merged
merged 16 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added

- Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108)
- Add client metric support to `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#4707)
- Add peer attributes to spans recorded by `NewClientHandler`, `NewServerHandler` in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#4873)

### Deprecated

- The `RequestCount`, `RequestContentLength`, `ResponseContentLength`, `ServerLatency` constants in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` are deprecated. (#4707)

### Fixed

- Do not panic in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` if `MeterProvider` returns a `nil` instrument. (#4875)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -256,6 +264,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,29 @@ 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"
Expand Down Expand Up @@ -105,6 +128,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -256,6 +264,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,29 @@ 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"
Expand Down Expand Up @@ -105,6 +128,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -256,6 +264,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,29 @@ 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"
Expand Down Expand Up @@ -105,6 +128,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
Expand Down
Loading