Skip to content

Commit f54fc6c

Browse files
committed
Add support for http client metrics
1 parent 3cecdcf commit f54fc6c

File tree

23 files changed

+1119
-46
lines changed

23 files changed

+1119
-46
lines changed

CHANGELOG.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1212

1313
- Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108)
1414
- Add `SDK.Shutdown` method in `"go.opentelemetry.io/contrib/config"`. (#4583)
15+
- Add client metric support to `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#4707)
1516

1617
### Changed
1718

@@ -23,9 +24,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
2324
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace` are upgraded to v1.20.0. (#4320)
2425
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/example` are upgraded to v1.20.0. (#4320)
2526
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/example` are upgraded to v1.20.0. (#4320)
26-
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`are upgraded to v1.20.0. (#4320)
27+
- The semantic conventions used by `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` are upgraded to v1.20.0. (#4320, #4707)
2728
- Updated configuration schema to include `schema_url` for resource definition and `without_type_suffix` and `without_units` for the Prometheus exporter. (#4727)
2829

30+
### Removed
31+
32+
- Remove `RequestCount`, `RequestContentLength`, `ResponseContentLength`, `ServerLatency` constants from `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#4707)
33+
2934
### Fixed
3035

3136
- Fix `NewServerHandler` in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` to correctly set the span status depending on the gRPC status. (#4587)

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv.go

+40
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue {
5151
return hc.ClientRequest(req)
5252
}
5353

54+
// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client.
55+
// The following attributes are always returned: "http.method", "net.peer.name".
56+
// The following attributes are returned if the
57+
// related values are defined in req: "net.peer.port".
58+
func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue {
59+
return hc.ClientRequestMetrics(req)
60+
}
61+
5462
// HTTPClientStatus returns a span status code and message for an HTTP status code
5563
// value received by a client.
5664
func HTTPClientStatus(code int) (codes.Code, string) {
@@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
286294
return attrs
287295
}
288296

297+
// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The
298+
// following attributes are always returned: "http.method", "net.peer.name".
299+
// The following attributes are returned if the related values
300+
// are defined in req: "net.peer.port".
301+
func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue {
302+
/* The following semantic conventions are returned if present:
303+
http.method string
304+
net.peer.name string
305+
net.peer.port int
306+
*/
307+
308+
n := 2 // method, peer name.
309+
var h string
310+
if req.URL != nil {
311+
h = req.URL.Host
312+
}
313+
peer, p := firstHostPort(h, req.Header.Get("Host"))
314+
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p)
315+
if port > 0 {
316+
n++
317+
}
318+
319+
attrs := make([]attribute.KeyValue, 0, n)
320+
attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer))
321+
322+
if port > 0 {
323+
attrs = append(attrs, c.NetConv.PeerPort(port))
324+
}
325+
326+
return attrs
327+
}
328+
289329
// ServerRequest returns attributes for an HTTP request received by a server.
290330
//
291331
// The server must be the primary server name if it is known. For example this

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,29 @@ func TestHTTPSClientRequest(t *testing.T) {
6868
)
6969
}
7070

71+
func TestHTTPSClientRequestMetrics(t *testing.T) {
72+
req := &http.Request{
73+
Method: http.MethodGet,
74+
URL: &url.URL{
75+
Scheme: "https",
76+
Host: "127.0.0.1:443",
77+
Path: "/resource",
78+
},
79+
Proto: "HTTP/1.0",
80+
ProtoMajor: 1,
81+
ProtoMinor: 0,
82+
}
83+
84+
assert.ElementsMatch(
85+
t,
86+
[]attribute.KeyValue{
87+
attribute.String("http.method", "GET"),
88+
attribute.String("net.peer.name", "127.0.0.1"),
89+
},
90+
HTTPClientRequestMetrics(req),
91+
)
92+
}
93+
7194
func TestHTTPClientRequest(t *testing.T) {
7295
const (
7396
user = "alice"
@@ -105,6 +128,40 @@ func TestHTTPClientRequest(t *testing.T) {
105128
)
106129
}
107130

131+
func TestHTTPClientRequestMetrics(t *testing.T) {
132+
const (
133+
user = "alice"
134+
n = 128
135+
agent = "Go-http-client/1.1"
136+
)
137+
req := &http.Request{
138+
Method: http.MethodGet,
139+
URL: &url.URL{
140+
Scheme: "http",
141+
Host: "127.0.0.1:8080",
142+
Path: "/resource",
143+
},
144+
Proto: "HTTP/1.0",
145+
ProtoMajor: 1,
146+
ProtoMinor: 0,
147+
Header: http.Header{
148+
"User-Agent": []string{agent},
149+
},
150+
ContentLength: n,
151+
}
152+
req.SetBasicAuth(user, "pswrd")
153+
154+
assert.ElementsMatch(
155+
t,
156+
[]attribute.KeyValue{
157+
attribute.String("http.method", "GET"),
158+
attribute.String("net.peer.name", "127.0.0.1"),
159+
attribute.Int("net.peer.port", 8080),
160+
},
161+
HTTPClientRequestMetrics(req),
162+
)
163+
}
164+
108165
func TestHTTPClientRequestRequired(t *testing.T) {
109166
req := new(http.Request)
110167
var got []attribute.KeyValue

instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv.go

+40
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue {
5151
return hc.ClientRequest(req)
5252
}
5353

54+
// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client.
55+
// The following attributes are always returned: "http.method", "net.peer.name".
56+
// The following attributes are returned if the
57+
// related values are defined in req: "net.peer.port".
58+
func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue {
59+
return hc.ClientRequestMetrics(req)
60+
}
61+
5462
// HTTPClientStatus returns a span status code and message for an HTTP status code
5563
// value received by a client.
5664
func HTTPClientStatus(code int) (codes.Code, string) {
@@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
286294
return attrs
287295
}
288296

297+
// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The
298+
// following attributes are always returned: "http.method", "net.peer.name".
299+
// The following attributes are returned if the related values
300+
// are defined in req: "net.peer.port".
301+
func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue {
302+
/* The following semantic conventions are returned if present:
303+
http.method string
304+
net.peer.name string
305+
net.peer.port int
306+
*/
307+
308+
n := 2 // method, peer name.
309+
var h string
310+
if req.URL != nil {
311+
h = req.URL.Host
312+
}
313+
peer, p := firstHostPort(h, req.Header.Get("Host"))
314+
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p)
315+
if port > 0 {
316+
n++
317+
}
318+
319+
attrs := make([]attribute.KeyValue, 0, n)
320+
attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer))
321+
322+
if port > 0 {
323+
attrs = append(attrs, c.NetConv.PeerPort(port))
324+
}
325+
326+
return attrs
327+
}
328+
289329
// ServerRequest returns attributes for an HTTP request received by a server.
290330
//
291331
// The server must be the primary server name if it is known. For example this

instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil/httpconv_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,29 @@ func TestHTTPSClientRequest(t *testing.T) {
6868
)
6969
}
7070

71+
func TestHTTPSClientRequestMetrics(t *testing.T) {
72+
req := &http.Request{
73+
Method: http.MethodGet,
74+
URL: &url.URL{
75+
Scheme: "https",
76+
Host: "127.0.0.1:443",
77+
Path: "/resource",
78+
},
79+
Proto: "HTTP/1.0",
80+
ProtoMajor: 1,
81+
ProtoMinor: 0,
82+
}
83+
84+
assert.ElementsMatch(
85+
t,
86+
[]attribute.KeyValue{
87+
attribute.String("http.method", "GET"),
88+
attribute.String("net.peer.name", "127.0.0.1"),
89+
},
90+
HTTPClientRequestMetrics(req),
91+
)
92+
}
93+
7194
func TestHTTPClientRequest(t *testing.T) {
7295
const (
7396
user = "alice"
@@ -105,6 +128,40 @@ func TestHTTPClientRequest(t *testing.T) {
105128
)
106129
}
107130

131+
func TestHTTPClientRequestMetrics(t *testing.T) {
132+
const (
133+
user = "alice"
134+
n = 128
135+
agent = "Go-http-client/1.1"
136+
)
137+
req := &http.Request{
138+
Method: http.MethodGet,
139+
URL: &url.URL{
140+
Scheme: "http",
141+
Host: "127.0.0.1:8080",
142+
Path: "/resource",
143+
},
144+
Proto: "HTTP/1.0",
145+
ProtoMajor: 1,
146+
ProtoMinor: 0,
147+
Header: http.Header{
148+
"User-Agent": []string{agent},
149+
},
150+
ContentLength: n,
151+
}
152+
req.SetBasicAuth(user, "pswrd")
153+
154+
assert.ElementsMatch(
155+
t,
156+
[]attribute.KeyValue{
157+
attribute.String("http.method", "GET"),
158+
attribute.String("net.peer.name", "127.0.0.1"),
159+
attribute.Int("net.peer.port", 8080),
160+
},
161+
HTTPClientRequestMetrics(req),
162+
)
163+
}
164+
108165
func TestHTTPClientRequestRequired(t *testing.T) {
109166
req := new(http.Request)
110167
var got []attribute.KeyValue

instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv.go

+40
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue {
5151
return hc.ClientRequest(req)
5252
}
5353

54+
// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client.
55+
// The following attributes are always returned: "http.method", "net.peer.name".
56+
// The following attributes are returned if the
57+
// related values are defined in req: "net.peer.port".
58+
func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue {
59+
return hc.ClientRequestMetrics(req)
60+
}
61+
5462
// HTTPClientStatus returns a span status code and message for an HTTP status code
5563
// value received by a client.
5664
func HTTPClientStatus(code int) (codes.Code, string) {
@@ -286,6 +294,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
286294
return attrs
287295
}
288296

297+
// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The
298+
// following attributes are always returned: "http.method", "net.peer.name".
299+
// The following attributes are returned if the related values
300+
// are defined in req: "net.peer.port".
301+
func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue {
302+
/* The following semantic conventions are returned if present:
303+
http.method string
304+
net.peer.name string
305+
net.peer.port int
306+
*/
307+
308+
n := 2 // method, peer name.
309+
var h string
310+
if req.URL != nil {
311+
h = req.URL.Host
312+
}
313+
peer, p := firstHostPort(h, req.Header.Get("Host"))
314+
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p)
315+
if port > 0 {
316+
n++
317+
}
318+
319+
attrs := make([]attribute.KeyValue, 0, n)
320+
attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer))
321+
322+
if port > 0 {
323+
attrs = append(attrs, c.NetConv.PeerPort(port))
324+
}
325+
326+
return attrs
327+
}
328+
289329
// ServerRequest returns attributes for an HTTP request received by a server.
290330
//
291331
// The server must be the primary server name if it is known. For example this

instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil/httpconv_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,29 @@ func TestHTTPSClientRequest(t *testing.T) {
6868
)
6969
}
7070

71+
func TestHTTPSClientRequestMetrics(t *testing.T) {
72+
req := &http.Request{
73+
Method: http.MethodGet,
74+
URL: &url.URL{
75+
Scheme: "https",
76+
Host: "127.0.0.1:443",
77+
Path: "/resource",
78+
},
79+
Proto: "HTTP/1.0",
80+
ProtoMajor: 1,
81+
ProtoMinor: 0,
82+
}
83+
84+
assert.ElementsMatch(
85+
t,
86+
[]attribute.KeyValue{
87+
attribute.String("http.method", "GET"),
88+
attribute.String("net.peer.name", "127.0.0.1"),
89+
},
90+
HTTPClientRequestMetrics(req),
91+
)
92+
}
93+
7194
func TestHTTPClientRequest(t *testing.T) {
7295
const (
7396
user = "alice"
@@ -105,6 +128,40 @@ func TestHTTPClientRequest(t *testing.T) {
105128
)
106129
}
107130

131+
func TestHTTPClientRequestMetrics(t *testing.T) {
132+
const (
133+
user = "alice"
134+
n = 128
135+
agent = "Go-http-client/1.1"
136+
)
137+
req := &http.Request{
138+
Method: http.MethodGet,
139+
URL: &url.URL{
140+
Scheme: "http",
141+
Host: "127.0.0.1:8080",
142+
Path: "/resource",
143+
},
144+
Proto: "HTTP/1.0",
145+
ProtoMajor: 1,
146+
ProtoMinor: 0,
147+
Header: http.Header{
148+
"User-Agent": []string{agent},
149+
},
150+
ContentLength: n,
151+
}
152+
req.SetBasicAuth(user, "pswrd")
153+
154+
assert.ElementsMatch(
155+
t,
156+
[]attribute.KeyValue{
157+
attribute.String("http.method", "GET"),
158+
attribute.String("net.peer.name", "127.0.0.1"),
159+
attribute.Int("net.peer.port", 8080),
160+
},
161+
HTTPClientRequestMetrics(req),
162+
)
163+
}
164+
108165
func TestHTTPClientRequestRequired(t *testing.T) {
109166
req := new(http.Request)
110167
var got []attribute.KeyValue

0 commit comments

Comments
 (0)