Skip to content

Commit

Permalink
Add http content size semantic conventions (#905)
Browse files Browse the repository at this point in the history
* Add http content size to standard package

Signed-off-by: Sam Xie <xsambundy@gmail.com>

* Include `http.request_content_length` in HTTP request basic attributes

Signed-off-by: Sam Xie <xsambundy@gmail.com>

* Add test for api.standard package

Signed-off-by: Sam Xie <xsambundy@gmail.com>

* Update CHANGELOG

Signed-off-by: Sam Xie <xsambundy@gmail.com>

* Fix http content size naming

Signed-off-by: Sam Xie <xsambundy@gmail.com>

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
  • Loading branch information
XSAM and MrAlias authored Jul 8, 2020
1 parent 1c9aab6 commit 03cd779
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 20 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Add `peer.service` semantic attribute. (#898)
- Add database-specific semantic attributes. (#899)
- Add semantic convention for `faas.coldstart` and `container.id`. (#909)
- Add http content size semantic conventions. (#905)
- Include `http.request_content_length` in HTTP request basic attributes. (#905)

### Changed

Expand Down Expand Up @@ -51,6 +53,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Ensure span status is not set to `Unknown` when no HTTP status code is provided as it is assumed to be `200 OK`. (#908)
- Ensure `httptrace.clientTracer` closes `http.headers` span. (#912)
- Prometheus exporter will not apply stale updates or forget inactive metrics. (#903)
- Add test for api.standard `HTTPClientAttributesFromHTTPRequest`. (#905)
- Bump github.com/golangci/golangci-lint from 1.27.0 to 1.28.1 in /tools. (#901, #913)
- Update otel-colector example to use the v0.5.0 collector. (#915)

Expand Down
3 changes: 3 additions & 0 deletions api/standard/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ func httpBasicAttributesFromHTTPRequest(request *http.Request) []kv.KeyValue {
if flavor != "" {
attrs = append(attrs, HTTPFlavorKey.String(flavor))
}
if request.ContentLength > 0 {
attrs = append(attrs, HTTPRequestContentLengthKey.Int64(request.ContentLength))
}

return attrs
}
Expand Down
211 changes: 203 additions & 8 deletions api/standard/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,14 +420,15 @@ func TestHTTPServerAttributesFromHTTPRequest(t *testing.T) {
serverName string
route string

method string
requestURI string
proto string
remoteAddr string
host string
url *url.URL
header http.Header
tls tlsOption
method string
requestURI string
proto string
remoteAddr string
host string
url *url.URL
header http.Header
tls tlsOption
contentLength int64

expected []otelkv.KeyValue
}
Expand Down Expand Up @@ -658,9 +659,22 @@ func TestHTTPServerAttributesFromHTTPRequest(t *testing.T) {
otelkv.String("http.client_ip", "1.2.3.4"),
},
},
{
name: "with content length",
method: "GET",
requestURI: "/user/123",
contentLength: 100,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "http"),
otelkv.Int64("http.request_content_length", 100),
},
},
}
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 := HTTPServerAttributesFromHTTPRequest(tc.serverName, tc.route, r)
assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name)
}
Expand Down Expand Up @@ -775,3 +789,184 @@ func kvStr(kvs []otelkv.KeyValue) string {
sb.WriteRune(']')
return sb.String()
}

func TestHTTPClientAttributesFromHTTPRequest(t *testing.T) {
testCases := []struct {
name string

method string
requestURI string
proto string
remoteAddr string
host string
url *url.URL
header http.Header
tls tlsOption
contentLength int64

expected []otelkv.KeyValue
}{
{
name: "stripped",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: noTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.url", "/user/123"),
otelkv.String("http.scheme", "http"),
otelkv.String("http.flavor", "1.0"),
},
},
{
name: "with tls",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.url", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.0"),
},
},
{
name: "with host",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.url", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.0"),
otelkv.String("http.host", "example.com"),
},
},
{
name: "with user agent",
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: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.url", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.0"),
otelkv.String("http.host", "example.com"),
otelkv.String("http.user_agent", "foodownloader"),
},
},
{
name: "with http 1.1",
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"},
},
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.url", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.1"),
otelkv.String("http.host", "example.com"),
otelkv.String("http.user_agent", "foodownloader"),
},
},
{
name: "with http 2",
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"},
},
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.url", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "2"),
otelkv.String("http.host", "example.com"),
otelkv.String("http.user_agent", "foodownloader"),
},
},
{
name: "with content length",
method: "GET",
url: &url.URL{
Path: "/user/123",
},
contentLength: 100,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.url", "/user/123"),
otelkv.String("http.scheme", "http"),
otelkv.Int64("http.request_content_length", 100),
},
},
{
name: "with empty method (fallback to GET)",
method: "",
url: &url.URL{
Path: "/user/123",
},
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.url", "/user/123"),
otelkv.String("http.scheme", "http"),
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls)
r.ContentLength = tc.contentLength
got := HTTPClientAttributesFromHTTPRequest(r)
assert.ElementsMatch(t, tc.expected, got)
})
}
}
14 changes: 14 additions & 0 deletions api/standard/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,20 @@ const (
// The IP address of the original client behind all proxies, if known
// (e.g. from X-Forwarded-For).
HTTPClientIPKey = kv.Key("http.client_ip")

// The size of the request payload body in bytes.
HTTPRequestContentLengthKey = kv.Key("http.request_content_length")

// The size of the uncompressed request payload body after transport decoding.
// Not set if transport encoding not used.
HTTPRequestContentLengthUncompressedKey = kv.Key("http.request_content_length_uncompressed")

// The size of the response payload body in bytes.
HTTPResponseContentLengthKey = kv.Key("http.response_content_length")

// The size of the uncompressed response payload body after transport decoding.
// Not set if transport encoding not used.
HTTPResponseContentLengthUncompressedKey = kv.Key("http.response_content_length_uncompressed")
)

var (
Expand Down
23 changes: 12 additions & 11 deletions instrumentation/httptrace/httptrace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,23 +87,24 @@ func TestRoundtrip(t *testing.T) {
address := ts.Listener.Addr()
hp := strings.Split(address.String(), ":")
expectedAttrs = map[kv.Key]string{
standard.HTTPFlavorKey: "1.1",
standard.HTTPHostKey: address.String(),
standard.HTTPMethodKey: "GET",
standard.HTTPSchemeKey: "http",
standard.HTTPTargetKey: "/",
standard.HTTPUserAgentKey: "Go-http-client/1.1",
standard.NetHostIPKey: hp[0],
standard.NetHostPortKey: hp[1],
standard.NetPeerIPKey: "127.0.0.1",
standard.NetTransportKey: "IP.TCP",
standard.HTTPFlavorKey: "1.1",
standard.HTTPHostKey: address.String(),
standard.HTTPMethodKey: "GET",
standard.HTTPSchemeKey: "http",
standard.HTTPTargetKey: "/",
standard.HTTPUserAgentKey: "Go-http-client/1.1",
standard.HTTPRequestContentLengthKey: "3",
standard.NetHostIPKey: hp[0],
standard.NetHostPortKey: hp[1],
standard.NetPeerIPKey: "127.0.0.1",
standard.NetTransportKey: "IP.TCP",
}

client := ts.Client()
err := tr.WithSpan(context.Background(), "test",
func(ctx context.Context) error {
ctx = correlation.ContextWithMap(ctx, correlation.NewMap(correlation.MapUpdate{SingleKV: kv.Key("foo").String("bar")}))
req, _ := http.NewRequest("GET", ts.URL, nil)
req, _ := http.NewRequest("GET", ts.URL, strings.NewReader("foo"))
httptrace.Inject(ctx, req)

res, err := client.Do(req)
Expand Down
4 changes: 3 additions & 1 deletion instrumentation/othttp/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -56,7 +57,7 @@ func TestHandlerBasics(t *testing.T) {
WithMeter(meter),
)

r, err := http.NewRequest(http.MethodGet, "http://localhost/", nil)
r, err := http.NewRequest(http.MethodGet, "http://localhost/", strings.NewReader("foo"))
if err != nil {
t.Fatal(err)
}
Expand All @@ -71,6 +72,7 @@ func TestHandlerBasics(t *testing.T) {
standard.HTTPSchemeHTTP,
standard.HTTPHostKey.String(r.Host),
standard.HTTPFlavorKey.String(fmt.Sprintf("1.%d", r.ProtoMinor)),
standard.HTTPRequestContentLengthKey.Int64(3),
}

assertMetricLabels(t, labelsToVerify, meterimpl.MeasurementBatches)
Expand Down

0 comments on commit 03cd779

Please sign in to comment.