diff --git a/proxy/proxy.go b/proxy/proxy.go index a024a2cc12..e711718085 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -521,6 +521,16 @@ func cloneHeaderExcluding(h http.Header, excludeList map[string]bool) http.Heade return hh } +func sizeOfHeader(h http.Header) (size int) { + for k, vv := range h { + size += len(k) + for _, v := range vv { + size += len(v) + } + } + return +} + type flusher struct { w flushedResponseWriter } @@ -1382,6 +1392,7 @@ func (p *Proxy) serveResponse(ctx *context) { p.tracing.logStreamEvent(ctx.proxySpan, StreamHeadersEvent, EndEvent) n, err := copyStream(ctx.responseWriter, ctx.response.Body) + p.tracing.setTag(ctx.proxySpan, HTTPResponseBodyCeil, ceilPow2(n)) p.tracing.logStreamEvent(ctx.proxySpan, StreamBodyEvent, strconv.FormatInt(n, 10)) if err != nil { p.metrics.IncErrorsStreaming(ctx.route.Id) @@ -1660,7 +1671,9 @@ func (p *Proxy) setCommonSpanInfo(u *url.URL, r *http.Request, s ot.Span) { setTag(s, HTTPMethodTag, r.Method). setTag(s, HostnameTag, p.hostname). setTag(s, HTTPPathTag, u.Path). - setTag(s, HTTPHostTag, r.Host) + setTag(s, HTTPHostTag, r.Host). + setTag(s, HTTPRequestHeaderCeil, ceilPow2(int64(sizeOfHeader(r.Header)))) + if val := r.Header.Get("X-Flow-Id"); val != "" { p.tracing.setTag(s, FlowIDTag, val) } diff --git a/proxy/tracing.go b/proxy/tracing.go index 66b481c8f3..efbe62ecbf 100644 --- a/proxy/tracing.go +++ b/proxy/tracing.go @@ -1,6 +1,8 @@ package proxy import ( + "math" + ot "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/tracing" @@ -19,6 +21,8 @@ const ( HTTPPathTag = "http.path" HTTPUrlTag = "http.url" HTTPStatusCodeTag = "http.status_code" + HTTPResponseBodyCeil = "http.response.body_ceil" + HTTPRequestHeaderCeil = "http.request.header_ceil" SkipperRouteIDTag = "skipper.route_id" SpanKindTag = "span.kind" @@ -135,3 +139,7 @@ func (t *filterTracing) logEnd(filterName string) { t.span.LogKV(filterName, EndEvent) } } + +func ceilPow2(n int64) int64 { + return int64(math.Pow(2, math.Ceil(math.Log2(float64(n))))) +} diff --git a/proxy/tracing_test.go b/proxy/tracing_test.go index 92d3ad3ff0..68d0c07c9d 100644 --- a/proxy/tracing_test.go +++ b/proxy/tracing_test.go @@ -76,8 +76,10 @@ func TestTracingIngressSpan(t *testing.T) { verifyTag(t, span, HostnameTag, "ingress.tracing.test") verifyTag(t, span, HTTPPathTag, "/hello") verifyTag(t, span, HTTPHostTag, ps.Listener.Addr().String()) + verifyTag(t, span, HTTPRequestHeaderCeil, int64(128)) verifyTag(t, span, FlowIDTag, "test-flow-id") verifyTag(t, span, HTTPStatusCodeTag, uint16(200)) + verifyNoTag(t, span, HTTPResponseBodyCeil) verifyHasTag(t, span, HTTPRemoteIPTag) } @@ -131,8 +133,10 @@ func TestTracingIngressSpanShunt(t *testing.T) { verifyTag(t, span, HostnameTag, "ingress-shunt.tracing.test") verifyTag(t, span, HTTPPathTag, "/hello") verifyTag(t, span, HTTPHostTag, ps.Listener.Addr().String()) + verifyTag(t, span, HTTPRequestHeaderCeil, int64(128)) verifyTag(t, span, FlowIDTag, "test-flow-id") verifyTag(t, span, HTTPStatusCodeTag, uint16(205)) + verifyNoTag(t, span, HTTPResponseBodyCeil) verifyHasTag(t, span, HTTPRemoteIPTag) } @@ -204,7 +208,9 @@ func TestTracingIngressSpanLoopback(t *testing.T) { verifyTag(t, span, HostnameTag, "ingress-loop.tracing.test") verifyTag(t, span, HTTPPathTag, paths[rid]) verifyTag(t, span, HTTPHostTag, ps.Listener.Addr().String()) + verifyTag(t, span, HTTPRequestHeaderCeil, int64(128)) verifyTag(t, span, FlowIDTag, "test-flow-id") + verifyNoTag(t, span, HTTPResponseBodyCeil) } } @@ -286,7 +292,8 @@ func TestTracingProxySpan(t *testing.T) { if err != nil { t.Error(err) } - w.WriteHeader(http.StatusNoContent) + w.WriteHeader(http.StatusOK) + w.Write([]byte("OK\n")) })) defer s.Close() @@ -310,10 +317,11 @@ func TestTracingProxySpan(t *testing.T) { } req.Header.Set("X-Flow-Id", "test-flow-id") - _, err = http.DefaultClient.Do(req) + resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } + resp.Body.Close() span := tracer.FindSpan("proxy") if span == nil { @@ -330,8 +338,10 @@ func TestTracingProxySpan(t *testing.T) { verifyTag(t, span, HostnameTag, "proxy.tracing.test") verifyTag(t, span, HTTPPathTag, "/bye") verifyTag(t, span, HTTPHostTag, backendAddr) + verifyTag(t, span, HTTPRequestHeaderCeil, int64(128)) verifyTag(t, span, FlowIDTag, "test-flow-id") - verifyTag(t, span, HTTPStatusCodeTag, uint16(204)) + verifyTag(t, span, HTTPStatusCodeTag, uint16(200)) + verifyTag(t, span, HTTPResponseBodyCeil, int64(4)) verifyNoTag(t, span, HTTPRemoteIPTag) } @@ -636,3 +646,21 @@ func verifyHasTag(t *testing.T, span *tracingtest.MockSpan, name string) { t.Errorf("expected '%s' tag", name) } } + +func TestCeilPow2(t *testing.T) { + assert.Equal(t, int64(0), ceilPow2(0)) + assert.Equal(t, int64(1), ceilPow2(1)) + assert.Equal(t, int64(2), ceilPow2(2)) + assert.Equal(t, int64(4), ceilPow2(3)) + assert.Equal(t, int64(4), ceilPow2(4)) + assert.Equal(t, int64(8), ceilPow2(5)) + assert.Equal(t, int64(8), ceilPow2(6)) + assert.Equal(t, int64(8), ceilPow2(7)) + assert.Equal(t, int64(8), ceilPow2(8)) + assert.Equal(t, int64(16), ceilPow2(9)) + + assert.Equal(t, int64(16384), ceilPow2(10_000)) + assert.Equal(t, int64(16384), ceilPow2(16_000)) + assert.Equal(t, int64(32768), ceilPow2(16_385)) + assert.Equal(t, int64(32768), ceilPow2(20_000)) +}