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

Add http.method attribute to http server metric #3018

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
The package contains semantic conventions from the `v1.12.0` version of the OpenTelemetry specification. (#3010)
- Add the `go.opentelemetry.io/otel/semconv/v1.11.0` package.
The package contains semantic conventions from the `v1.11.0` version of the OpenTelemetry specification. (#3009)
- Add http.method attribute to http server metric. (#3018)

## [1.8.0/0.31.0] - 2022-07-08

Expand Down
13 changes: 6 additions & 7 deletions semconv/internal/http.go
Expand Up @@ -147,12 +147,6 @@ func (sc *SemanticConventions) EndUserAttributesFromHTTPRequest(request *http.Re
func (sc *SemanticConventions) HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
attrs := []attribute.KeyValue{}

if request.Method != "" {
attrs = append(attrs, sc.HTTPMethodKey.String(request.Method))
} else {
attrs = append(attrs, sc.HTTPMethodKey.String(http.MethodGet))
}

// remove any username/password info that may be in the URL
// before adding it to the attributes
userinfo := request.URL.User
Expand Down Expand Up @@ -204,6 +198,12 @@ func (sc *SemanticConventions) httpBasicAttributesFromHTTPRequest(request *http.
attrs = append(attrs, sc.HTTPFlavorKey.String(flavor))
}

if request.Method != "" {
attrs = append(attrs, sc.HTTPMethodKey.String(request.Method))
} else {
attrs = append(attrs, sc.HTTPMethodKey.String(http.MethodGet))
}

return attrs
}

Expand All @@ -223,7 +223,6 @@ func (sc *SemanticConventions) HTTPServerMetricAttributesFromHTTPRequest(serverN
// supported.
func (sc *SemanticConventions) HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue {
attrs := []attribute.KeyValue{
sc.HTTPMethodKey.String(request.Method),
sc.HTTPTargetKey.String(request.RequestURI),
}

Expand Down
285 changes: 285 additions & 0 deletions semconv/internal/http_test.go
Expand Up @@ -1236,3 +1236,288 @@ func TestHTTPClientAttributesFromHTTPRequest(t *testing.T) {
})
}
}

func TestHTTPServerMetricAttributesFromHTTPRequest(t *testing.T) {
type testcase struct {
name string
serverName string
method string
requestURI string
proto string
remoteAddr string
host string
url *url.URL
header http.Header
tls tlsOption
contentLength int64
expected []attribute.KeyValue
}
testcases := []testcase{
{
name: "stripped",
serverName: "",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: noTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "http"),
attribute.String("http.flavor", "1.0"),
},
},
{
name: "with server name",
serverName: "my-server-name",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: noTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "http"),
attribute.String("http.flavor", "1.0"),
attribute.String("http.server_name", "my-server-name"),
},
},
{
name: "with tls",
serverName: "my-server-name",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: withTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "https"),
attribute.String("http.flavor", "1.0"),
attribute.String("http.server_name", "my-server-name"),
},
},
{
name: "with route",
serverName: "my-server-name",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: withTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "https"),
attribute.String("http.flavor", "1.0"),
attribute.String("http.server_name", "my-server-name"),
},
},
{
name: "with host",
serverName: "my-server-name",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: withTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "https"),
attribute.String("http.flavor", "1.0"),
attribute.String("http.server_name", "my-server-name"),
attribute.String("http.host", "example.com"),
},
},
{
name: "with host fallback",
serverName: "my-server-name",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Host: "example.com",
Path: "/user/123",
},
header: nil,
tls: withTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "https"),
attribute.String("http.flavor", "1.0"),
attribute.String("http.server_name", "my-server-name"),
attribute.String("http.host", "example.com"),
},
},
{
name: "with user agent",
serverName: "my-server-name",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: http.Header{
"User-Agent": []string{"foodownloader"},
},
tls: withTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "https"),
attribute.String("http.flavor", "1.0"),
attribute.String("http.server_name", "my-server-name"),
attribute.String("http.host", "example.com"),
},
},
{
name: "with proxy info",
serverName: "my-server-name",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: http.Header{
"User-Agent": []string{"foodownloader"},
"X-Forwarded-For": []string{"203.0.113.195, 70.41.3.18, 150.172.238.178"},
},
tls: withTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "https"),
attribute.String("http.flavor", "1.0"),
attribute.String("http.server_name", "my-server-name"),
attribute.String("http.host", "example.com"),
},
},
{
name: "with http 1.1",
serverName: "my-server-name",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.1",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: http.Header{
"User-Agent": []string{"foodownloader"},
"X-Forwarded-For": []string{"1.2.3.4"},
},
tls: withTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "https"),
attribute.String("http.flavor", "1.1"),
attribute.String("http.server_name", "my-server-name"),
attribute.String("http.host", "example.com"),
},
},
{
name: "with http 2",
serverName: "my-server-name",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/2.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: http.Header{
"User-Agent": []string{"foodownloader"},
"X-Forwarded-For": []string{"1.2.3.4"},
},
tls: withTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "https"),
attribute.String("http.flavor", "2"),
attribute.String("http.server_name", "my-server-name"),
attribute.String("http.host", "example.com"),
},
},
}
for idx, tc := range testcases {
r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls)
r.ContentLength = tc.contentLength
got := sc.HTTPServerMetricAttributesFromHTTPRequest(tc.serverName, r)
assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name)
}
}

func TestHttpBasicAttributesFromHTTPRequest(t *testing.T) {
type testcase struct {
name string
method string
requestURI string
proto string
remoteAddr string
host string
url *url.URL
header http.Header
tls tlsOption
contentLength int64
expected []attribute.KeyValue
}
testcases := []testcase{
{
name: "stripped",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: noTLS,
expected: []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "http"),
attribute.String("http.flavor", "1.0"),
attribute.String("http.host", "example.com"),
},
},
}
for idx, tc := range testcases {
r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls)
r.ContentLength = tc.contentLength
got := sc.httpBasicAttributesFromHTTPRequest(r)
assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name)
}
}